www.gusucode.com > VC++仿XP免费Prof UIS界面库-源码程序 > VC++仿XP免费Prof UIS界面库-源码程序/code/Samples/GLViews/ChildView.cpp

    //Download by http://www.NewXing.com
// ChildView.cpp : implementation of the CChildView class
//

#include "stdafx.h"
#include "GLViews.h"
#include "ChildView.h"
#include "MainFrm.h"

#include <io.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// C3DObject

IMPLEMENT_DYNCREATE( C3DObject, CObject );

C3DObject::C3DObject(
	LPCTSTR sName, // = GL_VIEWS_NONAME
	bool bEnabledParentDependency, //  = true
	bool bEnabledScale, //  = false
	bool bEnabledTranslation, //  = true
	bool bEnabledOrientation //  = true
	)
	: m_sName( sName )
	, m_pParent( NULL )
	, m_bEnabledParentDependency( bEnabledParentDependency )
	, m_bEnabledScale( bEnabledScale )
	, m_bEnabledTranslation( bEnabledTranslation )
	, m_bEnabledOrientation( bEnabledOrientation )
	, m_hti( NULL )
{
	ASSERT( sName != NULL );
	ASSERT( !m_sName.IsEmpty() );
	LocalLoadIdentity();
}

C3DObject::~C3DObject()
{
	ASSERT( m_pParent == NULL );
	for(	POSITION pos = m_listChilds.GetHeadPosition();
			pos != NULL;
			)
	{ // walk all childs
		C3DObject * pChild = m_listChilds.GetNext( pos );
		ASSERT_VALID( pChild );
		ASSERT( pChild->m_pParent == this );
		pChild->m_pParent = NULL;
		delete pChild;
	} // walk all childs
	m_listChilds.RemoveAll();
}

void C3DObject::SerializeState(
	CArchive & ar
	)
{
	ASSERT_VALID( this );
	if( ar.IsStoring() )
	{
		ar << m_v3LocalScale.x;
		ar << m_v3LocalScale.y;
		ar << m_v3LocalScale.z;
		
		ar << m_v3LocalTranslation.x;
		ar << m_v3LocalTranslation.y;
		ar << m_v3LocalTranslation.z;

		ar << m_quatLocalOrientation.x;
		ar << m_quatLocalOrientation.y;
		ar << m_quatLocalOrientation.z;
		ar << m_quatLocalOrientation.w;

//		ar << m_mtxLastTransformation.m11;
//		ar << m_mtxLastTransformation.m21;
//		ar << m_mtxLastTransformation.m31;
//		ar << m_mtxLastTransformation.m41;
//		ar << m_mtxLastTransformation.m12;
//		ar << m_mtxLastTransformation.m22;
//		ar << m_mtxLastTransformation.m32;
//		ar << m_mtxLastTransformation.m42;
//		ar << m_mtxLastTransformation.m13;
//		ar << m_mtxLastTransformation.m23;
//		ar << m_mtxLastTransformation.m33;
//		ar << m_mtxLastTransformation.m43;
//		ar << m_mtxLastTransformation.m14;
//		ar << m_mtxLastTransformation.m24;
//		ar << m_mtxLastTransformation.m34;
//		ar << m_mtxLastTransformation.m44;

		ar << m_sName;

		DWORD dwEnabledFlags = 0L;
		if( m_bEnabledParentDependency )
			dwEnabledFlags |= 0x00000001L;
		if( m_bEnabledScale )
			dwEnabledFlags |= 0x00000002L;
		if( m_bEnabledTranslation )
			dwEnabledFlags |= 0x00000004L;
		if( m_bEnabledOrientation )
			dwEnabledFlags |= 0x00000008L;

		ar << dwEnabledFlags;
	} // if( ar.IsStoring() )
	else
	{
		LocalLoadIdentity();

		ar >> m_v3LocalScale.x;
		ar >> m_v3LocalScale.y;
		ar >> m_v3LocalScale.z;
		
		ar >> m_v3LocalTranslation.x;
		ar >> m_v3LocalTranslation.y;
		ar >> m_v3LocalTranslation.z;

		ar >> m_quatLocalOrientation.x;
		ar >> m_quatLocalOrientation.y;
		ar >> m_quatLocalOrientation.z;
		ar >> m_quatLocalOrientation.w;

//		ar >> m_mtxLastTransformation.m11;
//		ar >> m_mtxLastTransformation.m21;
//		ar >> m_mtxLastTransformation.m31;
//		ar >> m_mtxLastTransformation.m41;
//		ar >> m_mtxLastTransformation.m12;
//		ar >> m_mtxLastTransformation.m22;
//		ar >> m_mtxLastTransformation.m32;
//		ar >> m_mtxLastTransformation.m42;
//		ar >> m_mtxLastTransformation.m13;
//		ar >> m_mtxLastTransformation.m23;
//		ar >> m_mtxLastTransformation.m33;
//		ar >> m_mtxLastTransformation.m43;
//		ar >> m_mtxLastTransformation.m14;
//		ar >> m_mtxLastTransformation.m24;
//		ar >> m_mtxLastTransformation.m34;
//		ar >> m_mtxLastTransformation.m44;

		ar >> m_sName;
		ASSERT( !m_sName.IsEmpty() );

		m_bEnabledParentDependency = false;
		m_bEnabledScale = false;
		m_bEnabledTranslation = false;
		m_bEnabledOrientation = false;
		DWORD dwEnabledFlags = 0L;
		ar >> dwEnabledFlags;
		if( (dwEnabledFlags&0x00000001L) != 0 )
			m_bEnabledParentDependency = true;
		if( (dwEnabledFlags&0x00000002L) != 0 )
			m_bEnabledScale = true;
		if( (dwEnabledFlags&0x00000004L) != 0 )
			m_bEnabledTranslation = true;
		if( (dwEnabledFlags&0x00000008L) != 0 )
			m_bEnabledOrientation = true;

	} // else from if( ar.IsStoring() )
}

void C3DObject::WalkTree(
	C3DObject::eWalkTreeQuery walkTreeQuery,
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror // = NULL
	)
{
	ASSERT_VALID( this );
	if(	!OnWalkTree(
			walkTreeQuery,
			pCam,
			pView3D,
			lpvCookie,
			pObjMirror
			)
		)
		return;
	for(	POSITION pos = m_listChilds.GetHeadPosition();
			pos != NULL;
			)
	{ // walk all childs
		C3DObject * pChild = m_listChilds.GetNext( pos );
		ASSERT_VALID( pChild );
		ASSERT( pChild->m_pParent == this );
		if(		walkTreeQuery == EWTQ_RENDER
			&&	( !IsRenderSubtreeItem(pChild) )
			)
			continue;
		pChild->WalkTree(
			walkTreeQuery,
			pCam,
			pView3D,
			lpvCookie,
			pObjMirror
			);
	} // walk all childs
}

bool C3DObject::OnWalkTree(
	C3DObject::eWalkTreeQuery walkTreeQuery,
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
bool bRetVal = true;
	ASSERT_VALID( this );
	switch( walkTreeQuery )
	{
	case EWTQ_THREAD_INIT:
		OnThreadInit( lpvCookie );
	break;
	case EWTQ_THREAD_DONE:
		OnThreadDone( lpvCookie );
	break;
	case EWTQ_PLAY:
		OnPlay( lpvCookie );
	break;
	case EWTQ_TRANSFORM:
		ASSERT_VALID( pCam );
		ASSERT_VALID( pView3D );
		OnTransform( pCam, pView3D, lpvCookie );
	break;
	case EWTQ_RENDER:
		ASSERT_VALID( pCam );
		ASSERT_VALID( pView3D );
		OnRender( pCam, pView3D, lpvCookie, pObjMirror );
		if( !IsRenderSubtree() )
			bRetVal = false;
	break;
	case EWTQ_ADD_TO_TREE:
		OnAddToTree( (CObjectHierarchyTreeCtrl*)lpvCookie );
	break;
	} // switch( walkTreeQuery )
	return bRetVal;
}

void C3DObject::OnThreadInit( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	lpvCookie;
}

void C3DObject::OnThreadDone( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	lpvCookie;
}

void C3DObject::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
}

void C3DObject::CalcTransformation(
	_mrct mtxParentTransformation
	)
{
	ASSERT_VALID( this );
	if( m_bEnabledParentDependency )
		m_mtxLastTransformation = mtxParentTransformation;
	else
		m_mtxLastTransformation.load_identity();
	if( m_bEnabledScale )
		m_mtxLastTransformation *= m_v3LocalScale.get_as_scale();
	if( m_bEnabledTranslation )
		m_mtxLastTransformation *= m_v3LocalTranslation.get_as_translation();
	if( m_bEnabledOrientation )
		m_mtxLastTransformation *= m_quatLocalOrientation;
}

void C3DObject::OnTransform(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
C3DObject * pParent = GetParent();
	if( pParent != NULL )
		CalcTransformation( pParent->m_mtxLastTransformation );
	else
	{
		_mt mtxIdentity;
		CalcTransformation( mtxIdentity );
	}
}

void C3DObject::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	pObjMirror;
}

bool C3DObject::IsRenderSubtree()
{
	ASSERT_VALID( this );
	return true;
}

bool C3DObject::IsRenderSubtreeItem( C3DObject * pObjChild )
{
	ASSERT_VALID( this );
	ASSERT_VALID( pObjChild );
	ASSERT( pObjChild->GetParent() == this );
	pObjChild;
	return true;
}

void C3DObject::OnAddToTree(
	CObjectHierarchyTreeCtrl * pTreeCtrl
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pTreeCtrl );
	ASSERT( pTreeCtrl->GetSafeHwnd() != NULL );
	ASSERT( ::IsWindow( pTreeCtrl->GetSafeHwnd() ) );
	ASSERT( m_hti == NULL );
HTREEITEM htiParent = TVI_ROOT;
C3DObject * pObjParent = GetParent();
	if( pObjParent != NULL )
	{
		ASSERT_VALID( pObjParent );
		htiParent = pObjParent->m_hti;
		ASSERT( htiParent != NULL );
	}
CString strTreeItemText;
int nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_GENERIC_OBJ;
	OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	ASSERT( !strTreeItemText.IsEmpty() );
	m_hti =
		pTreeCtrl->InsertItem(
			(LPCTSTR)strTreeItemText,
			nTreeImageIndex,
			nTreeImageIndex,
			htiParent,
			TVI_LAST
			);
	ASSERT( m_hti != NULL );
	VERIFY( pTreeCtrl->SetItemData( m_hti, (DWORD)this ) );
}

void C3DObject::OnTreeItemDblClick(
	CObjectHierarchyTreeCtrl * pTreeCtrl
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pTreeCtrl );
	ASSERT( pTreeCtrl->GetSafeHwnd() != NULL );
	ASSERT( ::IsWindow( pTreeCtrl->GetSafeHwnd() ) );
	ASSERT( m_hti != NULL );
	pTreeCtrl;
}

void C3DObject::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	strTreeItemText = GetName();
	ASSERT( !strTreeItemText.IsEmpty() );
	if( m_pParent == NULL )
		nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_ROOT;
}

void C3DObject::GenerateSphere(
	int nTextureIndex,
	_v3rct ptCenter,
	GLfloat fRadius,
	int nStacksCount,
	GLfloat fAngleTheta0,
	GLfloat fAngleTheta1,
	GLfloat fAngleTheta1Phi0,
	GLfloat fAngleTheta1Phi1,
	bool bNoDepth // = false
	)
{
	ASSERT( fRadius >= 0 && nStacksCount > 3 );
	for( int nStackIdx0 = 0; nStackIdx0 < (nStacksCount / 2); nStackIdx0++ )
	{
		GLfloat fAngleStack0 =
			fAngleTheta1Phi0 +
				( fAngleTheta1Phi1 - fAngleTheta1Phi0 ) /
					( nStacksCount / 2 ) * nStackIdx0 ;
		GLfloat fAngleStack1 =
			fAngleTheta1Phi0 +
				( fAngleTheta1Phi1 - fAngleTheta1Phi0 ) /
					( nStacksCount / 2 ) * ( nStackIdx0 + 1 );
		_v3t
			vNormalPrev0, vNormalCurr0, vNormalPrev1, vNormalCurr1,
			vVertexPrev0, vVertexCurr0, vVertexPrev1, vVertexCurr1;
		GLfloat
			fTextureCoordPrevX0 = 0.0f, fTextureCoordPrevY0 = 0.0f,
			fTextureCoordCurrX0 = 0.0f, fTextureCoordCurrY0 = 0.0f,
			fTextureCoordPrevX1 = 0.0f, fTextureCoordPrevY1 = 0.0f,
			fTextureCoordCurrX1 = 0.0f, fTextureCoordCurrY1 = 0.0f;
		for( int nStackIdx1 = 0; nStackIdx1 <= nStacksCount; nStackIdx1++ )
		{
			if( nStackIdx1 > 0 )
			{
				vNormalPrev0 = vNormalCurr0;
				vNormalPrev1 = vNormalCurr1;
				vVertexPrev0 = vVertexCurr0;
				vVertexPrev1 = vVertexCurr1;
				fTextureCoordPrevX0 = fTextureCoordCurrX0;
				fTextureCoordPrevX1 = fTextureCoordCurrX1;
				fTextureCoordPrevY0 = fTextureCoordCurrY0;
				fTextureCoordPrevY1 = fTextureCoordCurrY1;
			} // if( nStackIdx1 > 0 )
			GLfloat fAngleStack2 =
				fAngleTheta0 +
					nStackIdx1 * (fAngleTheta1 - fAngleTheta0)
						/ nStacksCount;
			vNormalCurr0.load_vector(
				_ntr::cos(fAngleStack0) * _ntr::cos(fAngleStack2),
				_ntr::sin(fAngleStack0),
				_ntr::cos(fAngleStack0) * _ntr::sin(fAngleStack2)
				);
			vVertexCurr0.load_vector(
				ptCenter.x + fRadius * vNormalCurr0.x,
				ptCenter.y + fRadius * vNormalCurr0.y,
				ptCenter.z + fRadius * vNormalCurr0.z
				);
			fTextureCoordCurrX0 = ((GLfloat)nStackIdx1) / ((GLfloat)nStacksCount);
			fTextureCoordCurrY0 = (2.0f * nStackIdx0) / ((GLfloat)nStacksCount);
			vNormalCurr1.load_vector(
				_ntr::cos(fAngleStack1) * _ntr::cos(fAngleStack2),
				_ntr::sin(fAngleStack1),
				_ntr::cos(fAngleStack1) * _ntr::sin(fAngleStack2)
				);
			vVertexCurr1.load_vector(
				ptCenter.x + fRadius * vNormalCurr1.x,
				ptCenter.y + fRadius * vNormalCurr1.y,
				ptCenter.z + fRadius * vNormalCurr1.z
				);
			fTextureCoordCurrX1 = ((GLfloat)nStackIdx1) / ((GLfloat)nStacksCount);
			fTextureCoordCurrY1 = (2 * (nStackIdx0 + 1)) / (GLfloat)nStacksCount;
			if( nStackIdx1 > 0 )
			{
				C3DSquare * pObjSquare = new C3DSquare;
				pObjSquare->m_nTextureIndex = nTextureIndex;
				pObjSquare->m_bNoDepth = bNoDepth;
				pObjSquare->m_bUseNormal = true;
				pObjSquare->m_vecNormal = vNormalCurr1;
				pObjSquare->m_arrTextureCoords[0].m_fX = fTextureCoordPrevX0;
				pObjSquare->m_arrTextureCoords[0].m_fY = fTextureCoordPrevY0;
				pObjSquare->m_arrPoints[0] = vVertexPrev0;
				pObjSquare->m_arrTextureCoords[1].m_fX = fTextureCoordPrevX1;
				pObjSquare->m_arrTextureCoords[1].m_fY = fTextureCoordPrevY1;
				pObjSquare->m_arrPoints[1] = vVertexPrev1;
				pObjSquare->m_arrTextureCoords[2].m_fX = fTextureCoordCurrX1;
				pObjSquare->m_arrTextureCoords[2].m_fY = fTextureCoordCurrY1;
				pObjSquare->m_arrPoints[2] = vVertexCurr1;
				pObjSquare->m_arrTextureCoords[3].m_fX = fTextureCoordCurrX0;
				pObjSquare->m_arrTextureCoords[3].m_fY = fTextureCoordCurrY0;
				pObjSquare->m_arrPoints[3] = vVertexCurr0;
				AddChild( pObjSquare );
			} // if( nStackIdx1 > 0 )
		} // for( int nStackIdx1 = 0; nStackIdx1 <= nStacksCount; nStackIdx1++ )
	} // for( int nStackIdx0 = 0; nStackIdx0 < (nStacksCount / 2); nStackIdx0++ )
}

bool C3DObject::stat_LoadResourceToMemory(
	LPCTSTR pszResId,
	LPCTSTR pszRsType,
	LPVOID * pLpvOutBuffer,
	DWORD * pDwOutSize
	)
{
	*pLpvOutBuffer = NULL;
	*pDwOutSize = 0L;
HINSTANCE hInst =
		::AfxFindResourceHandle( pszResId, pszRsType );
	if( hInst == NULL )
	{
		ASSERT( FALSE );
		return false;
	}
HRSRC hRsrc = FindResource( hInst, pszResId, pszRsType );
	if( hRsrc == NULL )
	{
		ASSERT( FALSE );
		return false;
	}
HGLOBAL hGlobal = LoadResource( hInst, hRsrc );
	if( hGlobal == NULL )
	{
		ASSERT( FALSE );
		return false;
	}
LPVOID lpvData = LockResource( hGlobal );
DWORD dwSize = SizeofResource( hInst, hRsrc );
	if( lpvData == NULL )
	{
		ASSERT( FALSE );
		::UnlockResource( hGlobal );
		::FreeResource( hGlobal );
		return false;
	}
	if( dwSize == 0L )
	{
		ASSERT( FALSE );
		::UnlockResource( hGlobal );
		::FreeResource( hGlobal );
		return false;
	}
	*pLpvOutBuffer = malloc( dwSize );
	if( (*pLpvOutBuffer) == NULL )
	{
		ASSERT( FALSE );
		::UnlockResource( hGlobal );
		::FreeResource( hGlobal );
		return false;
	}
	memcpy( *pLpvOutBuffer, lpvData, dwSize );
	*pDwOutSize = dwSize;
	::UnlockResource( hGlobal );
	::FreeResource( hGlobal );
	return true;
}

bool C3DObject::stat_IsFileExists(
	LPCTSTR sFilePath
	)
{
	ASSERT( sFilePath != NULL );
	if( sFilePath == NULL )
		return false;
#if (defined _UNICODE)
		struct _wfinddata_t fd;
#else
		struct _finddata_t fd;
#endif
	long hNextFile =
#if (defined _UNICODE)
			_wfindfirst(
#else
			_findfirst(
#endif
				(LPTSTR)sFilePath,
				&fd
				);
bool bExists = true;
	if( hNextFile < 0 )
		bExists = false;
	else
	{
		if( (fd.attrib&_A_SUBDIR) != 0 )
			bExists = false;
	} // else from if( hNextFile < 0 )
	_findclose( hNextFile );
	return bExists;
}

/////////////////////////////////////////////////////////////////////////////
// C3DModifier

IMPLEMENT_DYNCREATE( C3DModifier, C3DObject );

C3DModifier::C3DModifier(
	LPCTSTR sName, // = GL_VIEWS_NONAME
	GLfloat fAnglePlayStepPitch, //  = 0.0f
	GLfloat fAnglePlayStepYaw, //  = 0.0f
	GLfloat fAnglePlayStepRoll //  = 0.0f
	)
	: C3DObject( sName )
	, m_fAnglePlayStepPitch( fAnglePlayStepPitch )
	, m_fAnglePlayStepYaw( fAnglePlayStepYaw )
	, m_fAnglePlayStepRoll( fAnglePlayStepRoll )
{
}

C3DModifier::~C3DModifier()
{
}

void C3DModifier::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	ASSERT( !m_sName.IsEmpty() );
int nCount = m_listChilds.GetCount();
	ASSERT( nCount > 0 );
	if( nCount > 1 )
	{
		strTreeItemText = _T("Group modifier \"");
		strTreeItemText += m_sName;
		strTreeItemText += _T("\"");
		nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_MODIFIER_GRP;
		return;
	} // if( nCount > 1 )
C3DObject * pObjHead = m_listChilds.GetHead();
	ASSERT_VALID( pObjHead );
CString strChildDisplayText;
	pObjHead->OnQueryTreeDisplayParms(
		strChildDisplayText,
		nTreeImageIndex
		);
	ASSERT( !strChildDisplayText.IsEmpty() );
	strTreeItemText = _T("Modifier for: ");
	strTreeItemText += strChildDisplayText;
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_MODIFIER_ONE;
}

void C3DModifier::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
#ifdef GL_VIEWS_DRAW_ANIMS
	if(		m_fAnglePlayStepPitch != 0.0f
		&&	m_fAnglePlayStepYaw != 0.0f
		&&	m_fAnglePlayStepRoll != 0.0f
		)
		LocalAdjustOrientation(
			m_fAnglePlayStepPitch,
			m_fAnglePlayStepYaw,
			m_fAnglePlayStepRoll
			);
#endif // GL_VIEWS_DRAW_ANIMS
}

/////////////////////////////////////////////////////////////////////////////
// C3DSquare

IMPLEMENT_DYNCREATE( C3DSquare, C3DObject );

C3DSquare::C3DSquare()
	: C3DObject( GL_VIEWS_SQUARE_NAME )
	, m_nTextureIndex( -1 )
	, m_bUseNormal( false )
	, m_bNoDepth( false )
	, m_bNoCullFace( false )
	, m_bAdjustAlphaFunc( false )
	, m_bAdjustBlendFunc( false )
	, m_gleAlphaFunc( GL_NOTEQUAL )
	, m_glcAlphaRef( 0.0f )
	, m_gleBlendFactorS( GL_SRC_ALPHA )
	, m_gleBlendFactorD( GL_ONE )
{
}

C3DSquare::~C3DSquare()
{
}

void C3DSquare::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	pObjMirror;
	glPushMatrix();
	glMultMatrixf( m_mtxLastTransformation.arr );
		if( m_bNoDepth )
		{
			glDisable( GL_DEPTH_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bNoDepth )
		if( m_bNoCullFace )
		{
			glDisable( GL_CULL_FACE );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bNoCullFace )
		if( m_bAdjustAlphaFunc )
		{
			glEnable( GL_ALPHA_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glAlphaFunc( m_gleAlphaFunc, m_glcAlphaRef );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bAdjustAlphaFunc )
		if( m_bAdjustBlendFunc )
		{
			glEnable( GL_BLEND );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glBlendFunc( m_gleBlendFactorS, m_gleBlendFactorD );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bAdjustBlendFunc )
		if( m_nTextureIndex >= 0 )
		{
			ASSERT( m_nTextureIndex < GL_VIEWS_TEXTURE_COUNT );
			glEnable( GL_TEXTURE_2D );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glBindTexture(
				GL_TEXTURE_2D,
				pView3D->m_TextureIds[m_nTextureIndex]
				);
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_nTextureIndex >= 0 )
		glBegin(GL_QUADS);
			for( int nVertexIdx = 0; nVertexIdx < 4; nVertexIdx++ )
			{
				glTexCoord2f(
					m_arrTextureCoords[nVertexIdx].m_fX,
					m_arrTextureCoords[nVertexIdx].m_fY
					);
				glVertex3fv(
					m_arrPoints[nVertexIdx].arr
					);
			}
		glEnd();
		if( m_nTextureIndex >= 0 )
		{
			ASSERT( m_nTextureIndex < GL_VIEWS_TEXTURE_COUNT );
			glDisable( GL_TEXTURE_2D );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_nTextureIndex >= 0 )
		if( m_bAdjustBlendFunc )
		{
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glDisable( GL_BLEND );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bAdjustBlendFunc )
		if( m_bAdjustAlphaFunc )
		{
			glDisable( GL_ALPHA_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bAdjustAlphaFunc )
		if( m_bNoCullFace )
		{
			glEnable( GL_CULL_FACE );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bNoCullFace )
		if( m_bNoDepth )
		{
			glEnable( GL_DEPTH_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bNoDepth )
	glPopMatrix();
}

/////////////////////////////////////////////////////////////////////////////
// C3DOuterScene

IMPLEMENT_DYNCREATE( C3DOuterScene, C3DObject );

volatile bool C3DOuterScene::g_bRenderOuterScene = true;
GLfloat C3DOuterScene::g_fBottomPlaneValX = 0.7f;
GLfloat C3DOuterScene::g_fBottomPlaneValY = 0.4f;
GLfloat C3DOuterScene::g_fBottomPlaneValZ = 0.7f;
GLfloat C3DOuterScene::g_fRingRadius = 0.80f;
GLint C3DOuterScene::g_nSphereValStacks = 12;

C3DOuterScene::C3DOuterScene()
	: C3DObject( GL_VIEWS_OUTER_SCENE_NAME )
{
}

C3DOuterScene::~C3DOuterScene()
{
}

void C3DOuterScene::OnThreadInit( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	lpvCookie;

C3DSquare * pObjSquare = new C3DSquare;
	pObjSquare->m_nTextureIndex = GL_VIEWS_TEXTURE_IDX_BOTTOM_PLANE;
	pObjSquare->m_bNoDepth = true;
	pObjSquare->m_arrTextureCoords[3].m_fX = 2.0f;
	pObjSquare->m_arrTextureCoords[3].m_fY = 2.0f;
	pObjSquare->m_arrPoints[3] = _v3t( -g_fBottomPlaneValX, -g_fBottomPlaneValY, -g_fBottomPlaneValZ );
	pObjSquare->m_arrTextureCoords[2].m_fX = 0.0f;
	pObjSquare->m_arrTextureCoords[2].m_fY = 2.0f;
	pObjSquare->m_arrPoints[2] = _v3t(  g_fBottomPlaneValX, -g_fBottomPlaneValY, -g_fBottomPlaneValZ );
	pObjSquare->m_arrTextureCoords[1].m_fX = 0.0f;
	pObjSquare->m_arrTextureCoords[1].m_fY = 0.0f;
	pObjSquare->m_arrPoints[1] = _v3t(  g_fBottomPlaneValX, -g_fBottomPlaneValY,  g_fBottomPlaneValZ );
	pObjSquare->m_arrTextureCoords[0].m_fX = 2.0f;
	pObjSquare->m_arrTextureCoords[0].m_fY = 0.0f;
	pObjSquare->m_arrPoints[0] = _v3t( -g_fBottomPlaneValX, -g_fBottomPlaneValY,  g_fBottomPlaneValZ );
	pObjSquare->m_bUseNormal = true;
_v3t pt0(
	pObjSquare->m_arrPoints[1].x - pObjSquare->m_arrPoints[0].x,
	pObjSquare->m_arrPoints[1].y - pObjSquare->m_arrPoints[0].y,
	pObjSquare->m_arrPoints[1].z - pObjSquare->m_arrPoints[0].z
	);
_v3t pt1(
	pObjSquare->m_arrPoints[2].x - pObjSquare->m_arrPoints[0].x,
	pObjSquare->m_arrPoints[2].y - pObjSquare->m_arrPoints[0].y,
	pObjSquare->m_arrPoints[2].z - pObjSquare->m_arrPoints[0].z
	);
_v3t vNormal = pt0 ^ pt1;
	vNormal.normalize();
	pObjSquare->m_vecNormal = vNormal;
	AddChild( pObjSquare );

	GenerateSphere(
		GL_VIEWS_TEXTURE_IDX_RING,
		_v3t( 0.0f, 0.0f, 0.0f ),
		g_fRingRadius,
		g_nSphereValStacks,
		_ntr::get_pi()*2.0f,
		0.0f,
		- 0.3f,
		+ 0.7f,
		true
		);
}

bool C3DOuterScene::IsRenderSubtree()
{
	ASSERT_VALID( this );
	return g_bRenderOuterScene;
}

void C3DOuterScene::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	C3DObject::OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_OUTER_SCENE;
}

/////////////////////////////////////////////////////////////////////////////
// C3DCamera

IMPLEMENT_DYNCREATE( C3DCamera, C3DObject );

const GLfloat C3DCamera::g_arrFovValues[GL_VIEWS_FOV_COUNT] =
{
	(( 10.0f*3.1415926535897932384626433832795f)/180.0f ), //  0  10 degrees
	(( 15.0f*3.1415926535897932384626433832795f)/180.0f ), //  1  15 degrees
	(( 20.0f*3.1415926535897932384626433832795f)/180.0f ), //  2  20 degrees
	(( 25.0f*3.1415926535897932384626433832795f)/180.0f ), //  3  25 degrees
	(( 30.0f*3.1415926535897932384626433832795f)/180.0f ), //  4  30 degrees
	(( 35.0f*3.1415926535897932384626433832795f)/180.0f ), //  5  35 degrees
	(( 40.0f*3.1415926535897932384626433832795f)/180.0f ), //  6  40 degrees
	(( 45.0f*3.1415926535897932384626433832795f)/180.0f ), //  7  45 degrees
	(( 50.0f*3.1415926535897932384626433832795f)/180.0f ), //  8  50 degrees
	(( 55.0f*3.1415926535897932384626433832795f)/180.0f ), //  9  55 degrees
	(( 60.0f*3.1415926535897932384626433832795f)/180.0f ), // 10  60 degrees
	(( 65.0f*3.1415926535897932384626433832795f)/180.0f ), // 11  65 degrees
	(( 70.0f*3.1415926535897932384626433832795f)/180.0f ), // 12  70 degrees
	(( 75.0f*3.1415926535897932384626433832795f)/180.0f ), // 13  75 degrees
	(( 80.0f*3.1415926535897932384626433832795f)/180.0f ), // 14  80 degrees
	(( 85.0f*3.1415926535897932384626433832795f)/180.0f ), // 15  85 degrees
	(( 90.0f*3.1415926535897932384626433832795f)/180.0f ), // 16  90 degrees
	(( 95.0f*3.1415926535897932384626433832795f)/180.0f ), // 17  95 degrees
	((100.0f*3.1415926535897932384626433832795f)/180.0f ), // 18 100 degrees
	((110.0f*3.1415926535897932384626433832795f)/180.0f ), // 19 110 degrees
	((120.0f*3.1415926535897932384626433832795f)/180.0f ), // 20 120 degrees
	((130.0f*3.1415926535897932384626433832795f)/180.0f ), // 21 130 degrees
	((140.0f*3.1415926535897932384626433832795f)/180.0f ), // 22 140 degrees
	((150.0f*3.1415926535897932384626433832795f)/180.0f ), // 23 150 degrees
	((160.0f*3.1415926535897932384626433832795f)/180.0f ), // 24 160 degrees
	((170.0f*3.1415926535897932384626433832795f)/180.0f ), // 25 170 degrees
};

volatile bool C3DCamera::g_bRenderCameraAxes = true;
GLfloat C3DCamera::g_fAxisLineLength = 0.025f;
GLfloat C3DCamera::g_fAxisLineWidth = 2.0f;

C3DCamera::C3DCamera(
	UINT nTreeDblClickCmdID, // = 0L
	LPCTSTR sName, // = GL_VIEWS_NONAME
	int nFovIndex, // = GL_VIEWS_FOV_DEF_INDEX
	GLfloat fNearPlane, // = 1.0f
	GLfloat fFarPlane // = 5.0f
	)
	: C3DObject( sName )
	, m_nTreeDblClickCmdID( nTreeDblClickCmdID )
	, m_fAspect( 1.0f )
	, m_nFovIndex( nFovIndex )
	, m_fNearPlane( fNearPlane )
	, m_fFarPlane( fFarPlane )
{
	ASSERT( 0 <= m_nFovIndex && m_nFovIndex < GL_VIEWS_FOV_COUNT );
}

C3DCamera::~C3DCamera()
{
}

void C3DCamera::SerializeState(
	CArchive & ar
	)
{
	ASSERT_VALID( this );
	ASSERT( 0 <= m_nFovIndex && m_nFovIndex < GL_VIEWS_FOV_COUNT );
	C3DObject::SerializeState( ar );
	if( ar.IsStoring() )
	{
		ar << m_fAspect;
		ar << m_nFovIndex;
		ar << m_fNearPlane;
		ar << m_fFarPlane;
	} // if( ar.IsStoring() )
	else
	{
		ar >> m_fAspect;
		ar >> m_nFovIndex;
		ASSERT( 0 <= m_nFovIndex && m_nFovIndex < GL_VIEWS_FOV_COUNT );
		if( !(0 <= m_nFovIndex && m_nFovIndex < GL_VIEWS_FOV_COUNT) )
			m_nFovIndex = GL_VIEWS_FOV_DEF_INDEX;
		ar >> m_fNearPlane;
		ar >> m_fFarPlane;
	} // else from if( ar.IsStoring() )
}

void C3DCamera::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	if( (!g_bRenderCameraAxes) )
		return;
	if( pCam == this && pObjMirror == NULL )
		return;
	glPushMatrix();
	glMultMatrixf( m_mtxLastTransformation.arr );
		glLineWidth( g_fAxisLineWidth );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glBegin( GL_LINES );
			glColor3f( 1.0f, 0.0f, 0.0f );	glVertex3f( 0.0f, 0.0f, 0.0f );	glVertex3f( g_fAxisLineLength,              0.0f,              0.0f );
			glColor3f( 0.0f, 1.0f, 0.0f );	glVertex3f( 0.0f, 0.0f, 0.0f );	glVertex3f(              0.0f, g_fAxisLineLength,              0.0f );
			glColor3f( 0.0f, 0.0f, 1.0f );	glVertex3f( 0.0f, 0.0f, 0.0f );	glVertex3f(              0.0f,              0.0f, g_fAxisLineLength );
			glColor3f( 1.0f, 1.0f, 1.0f ); 
		glEnd();
	glPopMatrix();
}

void C3DCamera::OnTreeItemDblClick(
	CObjectHierarchyTreeCtrl * pTreeCtrl
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pTreeCtrl );
	ASSERT( pTreeCtrl->GetSafeHwnd() != NULL );
	ASSERT( ::IsWindow( pTreeCtrl->GetSafeHwnd() ) );
	ASSERT( m_hti != NULL );
	if( m_nTreeDblClickCmdID == 0L )
		return;
CFrameWnd * pFrame = pTreeCtrl->GetParentFrame();
	ASSERT_VALID( pFrame );
	if( pFrame->IsKindOf(RUNTIME_CLASS(CMiniFrameWnd)) )
	{
		pFrame = pFrame->GetParentFrame();
		ASSERT_VALID( pFrame );
		ASSERT( !pFrame->IsKindOf(RUNTIME_CLASS(CMiniFrameWnd)) );
	}
	pFrame->SendMessage( WM_COMMAND, m_nTreeDblClickCmdID );
}

void C3DCamera::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	C3DObject::OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_CAMERA_OBJ;
}

/////////////////////////////////////////////////////////////////////////////
// C3DCube

IMPLEMENT_DYNCREATE( C3DCube, C3DObject );

volatile bool C3DCube::g_bRenderCubeObjects = true;
GLfloat C3DCube::g_fCubeVal = 0.03f;
GLfloat C3DCube::g_fTranslateCubeVal = C3DCube::g_fCubeVal * 2.2f;

C3DCube::C3DCube(
	LPCTSTR sName, // = GL_VIEWS_NONAME
	bool bCenterCube // = false
	)
	: C3DObject( sName )
	, m_bCenterCube( bCenterCube )
{
}

C3DCube::~C3DCube()
{
}

GLfloat C3DCube::g_fCenterCubePlayPitch = _ntr::d2r(0.4f);
GLfloat C3DCube::g_fCenterCubePlayYaw = _ntr::d2r(0.3f);
GLfloat C3DCube::g_fCenterCubePlayRoll = _ntr::d2r(0.2f);

void C3DCube::OnThreadInit( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	lpvCookie;

static const struct
{
	int m_nTextureIndex;
	struct {
		GLfloat m_fTextureX, m_fTextureY,
			m_fPosX, m_fPosY, m_fPosZ;
	} m_arrVertexData[4];
} g_arrSquaresInitData[] = 
{
	{	GL_VIEWS_TEXTURE_IDX_CUBE_WHITE,
		{
			{ 0.0f, 0.0f, -g_fCubeVal, -g_fCubeVal,  g_fCubeVal },
			{ 1.0f, 0.0f,  g_fCubeVal, -g_fCubeVal,  g_fCubeVal },
			{ 1.0f, 1.0f,  g_fCubeVal,  g_fCubeVal,  g_fCubeVal },
			{ 0.0f, 1.0f, -g_fCubeVal,  g_fCubeVal,  g_fCubeVal },
		},
	},	
	{	GL_VIEWS_TEXTURE_IDX_CUBE_WHITE,
		{
			{ 1.0f, 0.0f, -g_fCubeVal, -g_fCubeVal, -g_fCubeVal },
			{ 1.0f, 1.0f, -g_fCubeVal,  g_fCubeVal, -g_fCubeVal },
			{ 0.0f, 1.0f,  g_fCubeVal,  g_fCubeVal, -g_fCubeVal },
			{ 0.0f, 0.0f,  g_fCubeVal, -g_fCubeVal, -g_fCubeVal },
		},
	},	
	{	GL_VIEWS_TEXTURE_IDX_CUBE_ORANGE,
		{
			{ 1.0f, 0.0f,  g_fCubeVal, -g_fCubeVal, -g_fCubeVal },
			{ 1.0f, 1.0f,  g_fCubeVal,  g_fCubeVal, -g_fCubeVal },
			{ 0.0f, 1.0f,  g_fCubeVal,  g_fCubeVal,  g_fCubeVal },
			{ 0.0f, 0.0f,  g_fCubeVal, -g_fCubeVal,  g_fCubeVal },
		},
	},	
	{	GL_VIEWS_TEXTURE_IDX_CUBE_ORANGE,
		{
			{ 0.0f, 0.0f, -g_fCubeVal, -g_fCubeVal, -g_fCubeVal },
			{ 1.0f, 0.0f, -g_fCubeVal, -g_fCubeVal,  g_fCubeVal },
			{ 1.0f, 1.0f, -g_fCubeVal,  g_fCubeVal,  g_fCubeVal },
			{ 0.0f, 1.0f, -g_fCubeVal,  g_fCubeVal, -g_fCubeVal },
		},
	},	
};

	for(	int nSquareIdx = 0;
			nSquareIdx < (sizeof(g_arrSquaresInitData)/sizeof(g_arrSquaresInitData[0]));
			nSquareIdx++
		)
	{
		C3DSquare * pObjSquare = new C3DSquare;
		pObjSquare->m_nTextureIndex =
			g_arrSquaresInitData[nSquareIdx].m_nTextureIndex;
		for( int nVertexIdx = 0; nVertexIdx < 4; nVertexIdx++ )
		{
			pObjSquare->m_arrTextureCoords[nVertexIdx].m_fX =
				g_arrSquaresInitData[nSquareIdx].m_arrVertexData[nVertexIdx].m_fTextureX;
			pObjSquare->m_arrTextureCoords[nVertexIdx].m_fY =
				g_arrSquaresInitData[nSquareIdx].m_arrVertexData[nVertexIdx].m_fTextureY;
			pObjSquare->m_arrPoints[nVertexIdx].x =
				g_arrSquaresInitData[nSquareIdx].m_arrVertexData[nVertexIdx].m_fPosX;
			pObjSquare->m_arrPoints[nVertexIdx].y =
				g_arrSquaresInitData[nSquareIdx].m_arrVertexData[nVertexIdx].m_fPosY;
			pObjSquare->m_arrPoints[nVertexIdx].z =
				g_arrSquaresInitData[nSquareIdx].m_arrVertexData[nVertexIdx].m_fPosZ;
		}
		pObjSquare->m_bNoCullFace = true;
		pObjSquare->m_bAdjustAlphaFunc = true;
		pObjSquare->m_bAdjustBlendFunc = true;
		AddChild( pObjSquare );
	}
}

void C3DCube::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
#ifdef GL_VIEWS_DRAW_ANIMS
	if( m_bCenterCube )
	{
		ASSERT_VALID( m_pParent );
		ASSERT_KINDOF( C3DModifier, m_pParent );
		m_pParent->LocalAdjustOrientation(
			g_fCenterCubePlayPitch,
			g_fCenterCubePlayYaw,
			g_fCenterCubePlayRoll
			);
	}
#endif // GL_VIEWS_DRAW_ANIMS
}

bool C3DCube::IsRenderSubtreeItem( C3DObject * pObjChild )
{
	ASSERT_VALID( this );
	ASSERT_VALID( pObjChild );
	ASSERT( pObjChild->GetParent() == this );
	if( g_bRenderCubeObjects )
		return true;
	if( pObjChild->IsKindOf(RUNTIME_CLASS(C3DSquare)) )
		return false;
	return true;
}

void C3DCube::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	strTreeItemText =
		m_bCenterCube
			? _T("center cube")
			: _T("leaf cube")
			;
	nTreeImageIndex =
		m_bCenterCube
			? GL_VIEWS_TREE_IMG_IDX_CUBE_CENTER
			: GL_VIEWS_TREE_IMG_IDX_CUBE_LEAF
			;
}

/////////////////////////////////////////////////////////////////////////////
// C3DPlanet

IMPLEMENT_DYNCREATE( C3DPlanet, C3DObject );

volatile bool C3DPlanet::g_bRenderPlanetObjects = true;
GLfloat C3DPlanet::g_fPlanetRadiusEarth = 0.04f;
GLfloat C3DPlanet::g_fPlanetRadiusMoon = 0.01f;
GLint C3DPlanet::g_nPlanetSphereStacks = 12;
GLfloat C3DPlanet::g_fAnglePlayStepPitchEarth = _ntr::d2r( 2.5f );
GLfloat C3DPlanet::g_fAnglePlayStepPitchMoon = _ntr::d2r( 4.5f );
GLfloat C3DPlanet::g_fAnglePlayStepYawEarth = _ntr::d2r( 1.5f );
GLfloat C3DPlanet::g_fAnglePlayStepYawMoon = _ntr::d2r( 2.5f );
GLfloat C3DPlanet::g_fAnglePlayStepRollEarth = _ntr::d2r( 4.0f );
GLfloat C3DPlanet::g_fAnglePlayStepRollMoon = _ntr::d2r( 6.0f );

C3DPlanet::C3DPlanet(
	LPCTSTR sName, // = GL_VIEWS_NONAME
	int nPlanetTextureIndex, // = GL_VIEWS_TEXTURE_IDX_EARTH
	GLint nPlanetStacks, // = C3DPlanet::g_nPlanetSphereStacks
	GLfloat fPlanetRadius, // = C3DPlanet::g_fPlanetRadiusEarth
	GLfloat fAnglePlayStepPitch, // = C3DPlanet::g_fAnglePlayStepPitchEarth
	GLfloat fAnglePlayStepYaw, // = C3DPlanet::g_fAnglePlayStepYawEarth
	GLfloat fAnglePlayStepRoll // = C3DPlanet::g_fAnglePlayStepRollMoon
	)
	: C3DObject( sName )
	, m_nPlanetTextureIndex( nPlanetTextureIndex )
	, m_nPlanetStacks( nPlanetStacks )
	, m_fPlanetRadius( fPlanetRadius )
	, m_fAnglePlayStepPitch( fAnglePlayStepPitch )
	, m_fAnglePlayStepYaw( fAnglePlayStepYaw )
	, m_fAnglePlayStepRoll( fAnglePlayStepRoll )
{
}

C3DPlanet::~C3DPlanet()
{
}

void C3DPlanet::OnThreadInit( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	lpvCookie;
	GenerateSphere(
		m_nPlanetTextureIndex,
		_v3t( 0.0f, 0.0f, 0.0f ),
		m_fPlanetRadius,
		m_nPlanetStacks,
		_ntr::get_pi() * 2.0f,
		0.0f,
		- _ntr::get_pi() / 2.0f,
		+ _ntr::get_pi() / 2.0f
		);
}

bool C3DPlanet::IsRenderSubtreeItem( C3DObject * pObjChild )
{
	ASSERT_VALID( this );
	ASSERT_VALID( pObjChild );
	ASSERT( pObjChild->GetParent() == this );
	if( g_bRenderPlanetObjects )
		return true;
	if( pObjChild->IsKindOf(RUNTIME_CLASS(C3DSquare)) )
		return false;
	return true;
}

void C3DPlanet::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	C3DObject::OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	//nTreeImageIndex = ....;
}

void C3DPlanet::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
	if( !g_bRenderPlanetObjects )
		return;
#ifdef GL_VIEWS_DRAW_ANIMS
	ASSERT_VALID( m_pParent );
	ASSERT_KINDOF( C3DModifier, m_pParent );
	m_pParent->LocalAdjustOrientation(
		m_fAnglePlayStepPitch,
		m_fAnglePlayStepYaw,
		m_fAnglePlayStepRoll
		);
#endif // GL_VIEWS_DRAW_ANIMS
}

/////////////////////////////////////////////////////////////////////////////
// C3DText

IMPLEMENT_DYNCREATE( C3DText, C3DObject );

volatile bool C3DText::g_bRenderTextObjects = true;
GLfloat C3DText::g_fScaleModifier = 0.03f;
GLfloat C3DText::g_fAnglePlayStepPitchText = 0.0f;
GLfloat C3DText::g_fAnglePlayStepYawText = 0.0f;
GLfloat C3DText::g_fAnglePlayStepRollText = _ntr::d2r( 2.0f );

C3DText::C3DText(
	LPCTSTR sName, // = GL_VIEWS_NONAME // should be used to set displayed text or one letter
	COLORREF clrText, // = RGB(255,255,255)
	GLfloat fAnglePlayStepPitch, // = C3DText::g_fAnglePlayStepPitchText
	GLfloat fAnglePlayStepYaw,   // = C3DText::g_fAnglePlayStepYawText
	GLfloat fAnglePlayStepRoll   // = C3DText::g_fAnglePlayStepRollText
	)
	: C3DObject( sName )
	, m_fRed(	GLfloat(GetRValue(clrText)) / 255.0f )
	, m_fGreen(	GLfloat(GetGValue(clrText)) / 255.0f )
	, m_fBlue(	GLfloat(GetBValue(clrText)) / 255.0f )
	, m_fAnglePlayStepPitch( fAnglePlayStepPitch )
	, m_fAnglePlayStepYaw( fAnglePlayStepYaw )
	, m_fAnglePlayStepRoll( fAnglePlayStepRoll )
{
}

C3DText::~C3DText()
{
}

void C3DText::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
	if( !g_bRenderTextObjects )
		return;
#ifdef GL_VIEWS_DRAW_ANIMS
	ASSERT_VALID( m_pParent );
	ASSERT_KINDOF( C3DModifier, m_pParent );
	m_pParent->LocalAdjustOrientation(
		m_fAnglePlayStepPitch,
		m_fAnglePlayStepYaw,
		m_fAnglePlayStepRoll
		);
#endif // GL_VIEWS_DRAW_ANIMS
}

void C3DText::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	lpvCookie;
	pObjMirror;
	if( !g_bRenderTextObjects )
		return;
	if( !pView3D->m_Font3D.IsFontCreated() )
		return;
LPCTSTR sText = GetName();
	ASSERT( sText != NULL );
	if( _tcslen(sText) == 0 )
		return;
	glEnable( GL_ALPHA_TEST );
	GL_VIEWS_CHECK_OPENGL_ERROR
	glAlphaFunc( GL_NOTEQUAL, 0.0 );
	GL_VIEWS_CHECK_OPENGL_ERROR
		glDisable( GL_CULL_FACE );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glPushMatrix();
			glMultMatrixf( m_mtxLastTransformation.arr );
				glColor3f( m_fRed, m_fGreen, m_fBlue );
					glEnable( GL_BLEND );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glBlendFunc( GL_SRC_ALPHA, GL_ONE );
					GL_VIEWS_CHECK_OPENGL_ERROR
						glPushMatrix();
							glScalef(
								g_fScaleModifier,
								g_fScaleModifier,
								g_fScaleModifier
								);
							pView3D->m_Font3D.TextOut( sText );
						glPopMatrix();
					glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glDisable( GL_BLEND );
					GL_VIEWS_CHECK_OPENGL_ERROR
				glColor3f( 1.0f, 1.0f, 1.0f ); 
			glPopMatrix();
		glDisable( GL_ALPHA_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
	glEnable( GL_CULL_FACE );
	GL_VIEWS_CHECK_OPENGL_ERROR
}

void C3DText::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	strTreeItemText.Format(
		_T("text \"%s\""),
		GetName()
		);
	ASSERT( !strTreeItemText.IsEmpty() );
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_TEXT_OBJ;
}

/////////////////////////////////////////////////////////////////////////////
// C3DWnd

IMPLEMENT_DYNCREATE( C3DWnd, C3DObject );

_v3t C3DWnd::g_arrDefPointsWndAviPlayer[4] =
{
	_v3t( -0.12f, -0.11f, -0.35f ),
	_v3t(  0.12f, -0.11f, -0.35f ),
	_v3t(  0.12f,  0.11f, -0.35f ),
	_v3t( -0.12f,  0.11f, -0.35f ),
};

_v3t C3DWnd::g_arrDefPointsWndMirror[4] =
{
	_v3t(  0.12f, -0.11f,  0.35f ),
	_v3t( -0.12f, -0.11f,  0.35f ),
	_v3t( -0.12f,  0.11f,  0.35f ),
	_v3t(  0.12f,  0.11f,  0.35f ),
};

C3DWnd::C3DWnd(
	LPCTSTR sName, // = GL_VIEWS_WND_AVI_PLAYER
	int nPlaneTextureIndex, // = GL_VIEWS_TEXTURE_IDX_WND_AVI_PLAYER
	_v3rct pt0, // = C3DWnd::g_arrDefPointsWndAviPlayer[0]
	_v3rct pt1, // = C3DWnd::g_arrDefPointsWndAviPlayer[1]
	_v3rct pt2, // = C3DWnd::g_arrDefPointsWndAviPlayer[2]
	_v3rct pt3  // = C3DWnd::g_arrDefPointsWndAviPlayer[3]
	)
	: C3DObject( sName )
	, m_nPlaneTextureIndex( nPlaneTextureIndex )
{
	m_arrWndPoints[0] = pt0;
	m_arrWndPoints[1] = pt1;
	m_arrWndPoints[2] = pt2;
	m_arrWndPoints[3] = pt3;
	_RecalcNormalAndContentArea();
}

C3DWnd::~C3DWnd()
{
}

void C3DWnd::_RecalcNormalAndContentArea()
{
	ASSERT_VALID( this );
_v3t pt0(
	m_arrWndPoints[1].x - m_arrWndPoints[0].x,
	m_arrWndPoints[1].y - m_arrWndPoints[0].y,
	m_arrWndPoints[1].z - m_arrWndPoints[0].z
	);
_v3t pt1(
	m_arrWndPoints[2].x - m_arrWndPoints[0].x,
	m_arrWndPoints[2].y - m_arrWndPoints[0].y,
	m_arrWndPoints[2].z - m_arrWndPoints[0].z
	);
	m_lastNormal = pt0 ^ pt1;
	m_lastNormal.normalize();

	stat_CalcWindowContentPlane(
		m_arrWndPoints,
		m_arrContentPoints
		);
}

void C3DWnd::stat_CalcWindowContentPlane(
	_v3t * arrWndPoints,
	_v3t * arrContentPoints
	)
{
	ASSERT( arrWndPoints != NULL );
	ASSERT( arrContentPoints != NULL );
	for( int i = 0; i< 4; i++ )
	{
//		arrContentPoints[i].x = arrWndPoints[i].x * 0.9700f;
//		arrContentPoints[i].y = arrWndPoints[i].y * 0.8000f;
//		arrContentPoints[i].z = arrWndPoints[i].z - arrWndPoints[i].z * 0.0001f;
		arrContentPoints[i].x = arrWndPoints[i].x * 0.9650f;
		arrContentPoints[i].y = arrWndPoints[i].y * ( (i < 2 ) ? 0.7850f : 0.7600f );
		arrContentPoints[i].z = arrWndPoints[i].z - arrWndPoints[i].z * 0.0001f;
	}
}

void C3DWnd::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	pObjMirror;
	glPushMatrix();
	glMultMatrixf( m_mtxLastTransformation.arr );
		glEnable( GL_ALPHA_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glAlphaFunc( GL_NOTEQUAL, 0.0f );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glEnable( GL_BLEND );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR );
			GL_VIEWS_CHECK_OPENGL_ERROR
				glEnable( GL_TEXTURE_2D );
				GL_VIEWS_CHECK_OPENGL_ERROR
					glBindTexture(
						GL_TEXTURE_2D,
						pView3D->m_TextureIds[m_nPlaneTextureIndex]
						);
					GL_VIEWS_CHECK_OPENGL_ERROR
					glDisable( GL_DEPTH_TEST );
					GL_VIEWS_CHECK_OPENGL_ERROR
						glBegin(GL_QUADS);
							glNormal3fv( m_lastNormal.arr );
							glTexCoord2f(0.0f, 0.0f); glVertex3fv( m_arrWndPoints[0].arr );
							glTexCoord2f(1.0f, 0.0f); glVertex3fv( m_arrWndPoints[1].arr );
							glTexCoord2f(1.0f, 1.0f); glVertex3fv( m_arrWndPoints[2].arr );
							glTexCoord2f(0.0f, 1.0f); glVertex3fv( m_arrWndPoints[3].arr );
						glEnd();
					glEnable( GL_DEPTH_TEST );
					GL_VIEWS_CHECK_OPENGL_ERROR
				glDisable( GL_TEXTURE_2D );
				GL_VIEWS_CHECK_OPENGL_ERROR
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glDisable( GL_BLEND );
			GL_VIEWS_CHECK_OPENGL_ERROR
		glDisable( GL_ALPHA_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
	glPopMatrix();
}

/////////////////////////////////////////////////////////////////////////////
// C3DMirror

IMPLEMENT_DYNCREATE( C3DMirror, C3DWnd );

volatile bool C3DMirror::g_bRenderMirrors = true;

C3DMirror::C3DMirror(
	LPCTSTR sName, // = GL_VIEWS_WND_MIRROR
	int nPlaneTextureIndex, // = GL_VIEWS_TEXTURE_IDX_WND_MIRROR
	_v3rct pt0, // = C3DWnd::g_arrDefPointsWndMirror[0]
	_v3rct pt1, // = C3DWnd::g_arrDefPointsWndMirror[1]
	_v3rct pt2, // = C3DWnd::g_arrDefPointsWndMirror[2]
	_v3rct pt3  // = C3DWnd::g_arrDefPointsWndMirror[3]
	)
	: C3DWnd( sName, nPlaneTextureIndex, pt0, pt1, pt2, pt3 )
	, m_bRenderingThisMirror( false )
{
}

C3DMirror::~C3DMirror()
{
}

void C3DMirror::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	if( m_bRenderingThisMirror )
		return;
	C3DWnd::OnRender( pCam, pView3D, lpvCookie, pObjMirror );
	if( !g_bRenderMirrors )
		return;
	if( pObjMirror != NULL )
		return; // multiply mirrors currently not supported by this sample
	m_bRenderingThisMirror = true;
		// clear mirror (make it black)
		glDisable( GL_DEPTH_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glPushMatrix();
			glMultMatrixf( m_mtxLastTransformation.arr );
				glBegin( GL_QUADS );
					glColor3f( 0.0f, 0.0f, 0.0f );
						glVertex3fv( m_arrContentPoints[0].arr );
						glVertex3fv( m_arrContentPoints[1].arr );
						glVertex3fv( m_arrContentPoints[2].arr );
						glVertex3fv( m_arrContentPoints[3].arr );
					glColor3f( 1.0f, 1.0f, 1.0f );
				glEnd();
			glPopMatrix();
		glEnable( GL_DEPTH_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
		// create content mask in the stencil buffer
		glEnable( GL_STENCIL_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glStencilFunc( GL_ALWAYS, 1, 1 );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glDisable( GL_DEPTH_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
				glPushMatrix();
				glMultMatrixf( m_mtxLastTransformation.arr );
					glBegin( GL_QUADS );
						glVertex3fv( m_arrContentPoints[0].arr );
						glVertex3fv( m_arrContentPoints[1].arr );
						glVertex3fv( m_arrContentPoints[2].arr );
						glVertex3fv( m_arrContentPoints[3].arr );
					glEnd();
				glPopMatrix();
			glEnable( GL_DEPTH_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
			GL_VIEWS_CHECK_OPENGL_ERROR
		// compute reflection matrix and render mirror content
		glStencilFunc( GL_EQUAL, 1, 1 );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glPushMatrix();
				_mt _mtxReflection;
// TODO: calc dynamically m_arrContentPoints[0].z for _ptEq (plane equation)
_v4t _ptEq( m_lastNormal.x, m_lastNormal.y, m_lastNormal.z, m_arrContentPoints[0].z );
//_v4t _ptEq( 0.0f, 0.0f, -1.0f, 0.35f );
				_mtxReflection.load_reflection( _ptEq );
				glMultMatrixf( _mtxReflection.arr );
				double _ptEqDouble[4] = { _ptEq.a, _ptEq.b, _ptEq.c, _ptEq.d };
				glClipPlane( GL_CLIP_PLANE0, _ptEqDouble );
				GL_VIEWS_CHECK_OPENGL_ERROR
				glEnable( GL_CLIP_PLANE0 );
				GL_VIEWS_CHECK_OPENGL_ERROR
					glCullFace( GL_BACK );
					GL_VIEWS_CHECK_OPENGL_ERROR
						the3DPipe.GetRoot()->WalkTree(
							C3DObject::EWTQ_RENDER,
							pCam,
							pView3D,
							lpvCookie,
							this
							);
					glCullFace( GL_FRONT );
					GL_VIEWS_CHECK_OPENGL_ERROR
				glDisable( GL_CLIP_PLANE0 );
				GL_VIEWS_CHECK_OPENGL_ERROR
			glPopMatrix();
		glDisable( GL_STENCIL_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
	m_bRenderingThisMirror = false;
}

void C3DMirror::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	C3DObject::OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_WND_MIRROR;
}

/////////////////////////////////////////////////////////////////////////////
// C3DAviPlayer

IMPLEMENT_DYNCREATE( C3DAviPlayer, C3DWnd );

volatile bool C3DAviPlayer::g_bRenderAviPlayers = true;

static LPCTSTR g_sAviPlayerTempFilePath = _T(".\\AVITEMP.AVI");

C3DAviPlayer::C3DAviPlayer(
	LPCTSTR sName, // = GL_VIEWS_WND_AVI_PLAYER
	int nPlaneTextureIndex, // = GL_VIEWS_TEXTURE_IDX_WND_AVI_PLAYER
	int nSurfaceTextureIndex, // = GL_VIEWS_TEXTURE_IDX_AVI_SURFACE
	_v3rct pt0, // = C3DWnd::g_arrDefPointsWndAviPlayer[0]
	_v3rct pt1, // = C3DWnd::g_arrDefPointsWndAviPlayer[1]
	_v3rct pt2, // = C3DWnd::g_arrDefPointsWndAviPlayer[2]
	_v3rct pt3, // = C3DWnd::g_arrDefPointsWndAviPlayer[3]
	int nRenderWidth, // = 256
	int nRenderHeight // = 256
	)
	: C3DWnd( sName, nPlaneTextureIndex, pt0, pt1, pt2, pt3 )
	, m_bInitComplete( false )
	, m_pAviStream( NULL )
	, m_pGetFrame( NULL )
	, m_nAviWidth( 0 )
	, m_nAviHeight( 0 )
	, m_nRenderWidth( nRenderWidth )
	, m_nRenderHeight( nRenderHeight )
	, m_nRenderSize( nRenderWidth*nRenderHeight )
	, m_hDrawDib( NULL )
	, m_nFrameNumber( 0 )
	, m_nFrameCount( 0 )
	, m_hDC( NULL )
	, m_hBitmap( NULL )
	, m_pDibRawData( NULL )
	, m_nSurfaceTextureIndex( nSurfaceTextureIndex )
{
	ATLASSERT( m_nRenderWidth > 0 );
	ATLASSERT( m_nRenderHeight > 0 );
	ATLASSERT( m_nRenderSize > 0 );
}

C3DAviPlayer::~C3DAviPlayer()
{
	ASSERT( !m_bInitComplete );
}

void C3DAviPlayer::stat_AlertDisplay( LPCTSTR sText )
{
	ASSERT( sText != NULL );
	::AfxMessageBox( sText, MB_OK|MB_ICONERROR );
}

void C3DAviPlayer::OnThreadInit( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	C3DObject::OnThreadInit( lpvCookie );
	ATLASSERT( m_nRenderWidth > 0 );
	ATLASSERT( m_nRenderHeight > 0 );
	ATLASSERT( m_nRenderSize > 0 );
	
	if( !stat_IsFileExists(g_sAviPlayerTempFilePath) )
	{
		LPVOID lpvBuffer = NULL;
		DWORD dwSize = 0;
		if(		!stat_LoadResourceToMemory(
					MAKEINTRESOURCE(IDR_AVIFILE_FOR_PLAYER),
					_T("AVI"),
					&lpvBuffer,
					&dwSize
					)
			||	lpvBuffer == NULL
			||	dwSize == 0
			)
		{
			ASSERT( FALSE );
			stat_AlertDisplay( _T("Failed to load AVI resource") );
			return;
		}
	
		bool bFileSaved = false;
		try
		{
			CFile _file(
				g_sAviPlayerTempFilePath,
				CFile::modeCreate
					|CFile::modeWrite
					|CFile::typeBinary
					|CFile::shareExclusive
				);
			_file.Seek( 0, CFile::begin );
			_file.Write( lpvBuffer, dwSize );
			_file.Close();
			bFileSaved = true;
		} // try
		catch( CException * pXept )
		{
			pXept->Delete();
			ASSERT( FALSE );
		} // catch( CException * pXept )
		catch( ... )
		{
			ASSERT( FALSE );
		} // catch( ... )

		::free( lpvBuffer );

		if( !bFileSaved )
		{
			ASSERT( FALSE );
			stat_AlertDisplay( _T("Failed to extract AVI resource") );
			return;
		}
	} // if( !statIsFileExists(sFilePath) )
	
	::AVIFileInit();
	if( ::AVIStreamOpenFromFile(
			&m_pAviStream,
			g_sAviPlayerTempFilePath,
			streamtypeVIDEO,
			0,
			OF_READ,
			NULL
			) != 0
		)
	{
		ASSERT( FALSE );
		stat_AlertDisplay( _T("AVI stream initialization failed") );
		return;
	}
	::AVIStreamInfo(
		m_pAviStream,
		&m_pAviInfo,
		sizeof(m_pAviInfo)
		);
	m_nAviWidth =
		m_pAviInfo.rcFrame.right - m_pAviInfo.rcFrame.left;
	m_nAviHeight =
		m_pAviInfo.rcFrame.bottom - m_pAviInfo.rcFrame.top;
	m_nFrameCount =
		::AVIStreamLength( m_pAviStream );
	m_hDC =
		::CreateCompatibleDC( NULL );
	if( m_hDC == NULL )
	{
		ASSERT( FALSE );
		stat_AlertDisplay( _T("Failed to alloc HDC for AVI stream") );
		return;
	}
	::memset( &m_dataBmpInfoHdr, 0, sizeof(BITMAPINFOHEADER) );
	m_dataBmpInfoHdr.biSize = sizeof(BITMAPINFOHEADER);
	m_dataBmpInfoHdr.biPlanes = 1;
	m_dataBmpInfoHdr.biBitCount = 32; //24;
	m_dataBmpInfoHdr.biWidth = m_nRenderWidth;
	m_dataBmpInfoHdr.biHeight = m_nRenderHeight;
	m_dataBmpInfoHdr.biCompression = BI_RGB;
	m_hBitmap =
		::CreateDIBSection(
			m_hDC,
			(BITMAPINFO*)( &m_dataBmpInfoHdr ),
			DIB_RGB_COLORS,
			(void**)(&m_pDibRawData),
			NULL,
			NULL
			);
	if( m_hBitmap == NULL )
	{
		ASSERT( FALSE );
		stat_AlertDisplay( _T("Failed to create DIB section") );
		return;
	}
	ATLASSERT( m_pDibRawData != NULL );
	::SelectObject( m_hDC, m_hBitmap );
	m_pGetFrame =
		::AVIStreamGetFrameOpen(
			m_pAviStream,
			NULL
			);
	if( m_pGetFrame == NULL )
	{
		ASSERT( FALSE );
		stat_AlertDisplay( _T("AVI frame initialization failed") );
		return;
	}
	m_hDrawDib = ::DrawDibOpen();
	if( m_hDrawDib == NULL )
	{
		ASSERT( FALSE );
		stat_AlertDisplay( _T("DrawDibOpen() failed") );
		return;
	}
	m_bInitComplete = true;
}

void C3DAviPlayer::OnThreadDone( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	C3DObject::OnThreadDone( lpvCookie );
	if( m_hBitmap != NULL )
		::DeleteObject( m_hBitmap );
	if( m_hDrawDib != NULL )
		::DrawDibClose( m_hDrawDib );
	if( m_pGetFrame != NULL )
		::AVIStreamGetFrameClose( m_pGetFrame );
	if( m_pAviStream != NULL )
		::AVIStreamRelease( m_pAviStream );
	::AVIFileExit();
	try
	{
		CFile::Remove( g_sAviPlayerTempFilePath );
	} // try
	catch( CException * pXept )
	{
		pXept->Delete();
		ASSERT( FALSE );
	} // catch( CException * pXept )
	catch( ... )
	{
		ASSERT( FALSE );
	} // catch( ... )
	m_bInitComplete = false;
}

void C3DAviPlayer::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	C3DWnd::OnRender( pCam, pView3D, lpvCookie, pObjMirror );
	if( (!g_bRenderAviPlayers) || (!m_bInitComplete) )
		return;
	glPushMatrix();
	glMultMatrixf( m_mtxLastTransformation.arr );
		glEnable( GL_TEXTURE_2D );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glBindTexture(
				GL_TEXTURE_2D,
				pView3D->m_TextureIds[m_nSurfaceTextureIndex]
				);
			GL_VIEWS_CHECK_OPENGL_ERROR
			////////// begin get AVI frame //////////
				LPBITMAPINFOHEADER lpbi;
				lpbi = (LPBITMAPINFOHEADER)
					::AVIStreamGetFrame(
						m_pGetFrame,
						m_nFrameNumber
						);
				unsigned char * pAviFrameRawData = (unsigned char *)
					lpbi
					+ lpbi->biSize
					+ lpbi->biClrUsed * sizeof(RGBQUAD);
				::DrawDibDraw(
					m_hDrawDib,
					m_hDC,
					0, 0, m_nRenderWidth, m_nRenderHeight,
					lpbi,
					pAviFrameRawData,
					0, 0, m_nAviWidth, m_nAviHeight,
					0
					);
				unsigned char * pBufferRGBA = m_pDibRawData;
				for( int i = 0; i < m_nRenderSize; i++ )
				{
					unsigned char _byte = *pBufferRGBA;
					*pBufferRGBA = *(pBufferRGBA+2);
					pBufferRGBA ++;
					pBufferRGBA ++;
					*pBufferRGBA = _byte;
					pBufferRGBA ++;
					*pBufferRGBA = 255;
					pBufferRGBA ++;
				}
				glTexSubImage2D(
					GL_TEXTURE_2D, 0,
					0, 0, m_nRenderWidth, m_nRenderHeight,
					GL_RGBA, GL_UNSIGNED_BYTE,
					m_pDibRawData
					);
				GL_VIEWS_CHECK_OPENGL_ERROR
			////////// end get AVI frame //////////
			glBegin( GL_QUADS );
				glTexCoord2f( 0.0f, 0.0f ); glVertex3fv( m_arrContentPoints[0].arr );
				glTexCoord2f( 1.0f, 0.0f ); glVertex3fv( m_arrContentPoints[1].arr );
				glTexCoord2f( 1.0f, 1.0f ); glVertex3fv( m_arrContentPoints[2].arr );
				glTexCoord2f( 0.0f, 1.0f ); glVertex3fv( m_arrContentPoints[3].arr );
			glEnd();
		glDisable( GL_TEXTURE_2D );
		GL_VIEWS_CHECK_OPENGL_ERROR
	glPopMatrix();
}

void C3DAviPlayer::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	C3DObject::OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_WND_AVI;
}

void C3DAviPlayer::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
#ifdef GL_VIEWS_DRAW_ANIMS
	if( m_bInitComplete && g_bRenderAviPlayers )
	{
		m_nFrameNumber ++;
		if( m_nFrameNumber >= m_nFrameCount )
			m_nFrameNumber = 0;
	} // if( m_bInitComplete && g_bRenderAviPlayers )
#endif // GL_VIEWS_DRAW_ANIMS
}

/////////////////////////////////////////////////////////////////////////////
// C3DTexture

void C3DTexture::_Destroy()
{
	if( m_sizeTexture.cx == 0 )
	{
		ASSERT( m_sizeTexture.cy == 0 );
		ASSERT( m_pData == NULL );
		return;
	}
	ASSERT( m_sizeTexture.cy != 0 );
	ASSERT( m_pData != NULL );
	free( m_pData );
	m_sizeTexture.cx = m_sizeTexture.cy = 0;
	m_pData = NULL;
	m_bAlphaLayerExist = false;
}

C3DTexture::C3DTexture()
	: m_sizeTexture( 0, 0 )
	, m_pData( NULL )
	, m_bAlphaLayerExist( false )
{
}

C3DTexture::~C3DTexture()
{
	_Destroy();
}

bool C3DTexture::LoadResourceBitmapAs32Bit(
	LPCTSTR lpszResource,
	unsigned char nAlphaClearVal // = 255
	)
{
	Empty();
CBitmap _bmp, _dib, *pBmpOldSrc = NULL, *pBmpOldDst = NULL;
	if( !_bmp.LoadBitmap(lpszResource) )
	{
		ASSERT( FALSE );
		return false;
	}
BITMAP _bmp_info;
	_bmp.GetBitmap( &_bmp_info );
	if(		_bmp_info.bmPlanes != 1
		||	_bmp_info.bmWidth <= 0
		||	_bmp_info.bmHeight <= 0
		)
	{
		ASSERT( FALSE );
		return false;
	}
const int nBytesPerPixel = 4;
int nPixelCount = _bmp_info.bmWidth * _bmp_info.bmHeight;
int nBytesCount = nPixelCount * nBytesPerPixel;

CWindowDC dcDesktop( NULL );
CDC dcSrc, dcDst;
	if(		!dcSrc.CreateCompatibleDC(&dcDesktop)
		||	!dcDst.CreateCompatibleDC(&dcDesktop)
		)
	{
		ASSERT( FALSE );
		return false;
	}
BITMAPINFOHEADER bih;
	::memset( (LPVOID)&bih, 0, sizeof(BITMAPINFOHEADER) );
	bih.biSize = sizeof(BITMAPINFOHEADER);
	bih.biWidth = _bmp_info.bmWidth;
	bih.biHeight = _bmp_info.bmHeight;
	bih.biPlanes = 1;
	bih.biBitCount = 32; /*_bmp_info.bmBitsPixel*/;
	bih.biCompression = BI_RGB;
	bih.biSizeImage = nPixelCount;
LPVOID pColorSurface = NULL;
HBITMAP	hDIB =
		::CreateDIBSection(
			dcDesktop.GetSafeHdc(),
			(LPBITMAPINFO)&bih,
			DIB_RGB_COLORS,
			&pColorSurface,
			NULL,
			NULL
			);
	if( hDIB == NULL || pColorSurface == NULL )
	{
		ASSERT( FALSE );
		return false;
	}
	_dib.Attach( hDIB );
	pBmpOldSrc = dcSrc.SelectObject( &_bmp );
	pBmpOldDst = dcDst.SelectObject( &_dib );
	dcDst.BitBlt(
		0, 0, _bmp_info.bmWidth, _bmp_info.bmHeight,
		&dcSrc, 
		0, 0,
		SRCCOPY
		);
	dcDst.SelectObject( pBmpOldDst );
	dcSrc.SelectObject( pBmpOldSrc );
	m_pData = ::malloc( nBytesCount );
	if( m_pData == NULL )
	{
		ASSERT( FALSE );
		return false;
	}
	::memcpy( m_pData, pColorSurface, nBytesCount );
	m_sizeTexture.cx = _bmp_info.bmWidth;
	m_sizeTexture.cy = _bmp_info.bmHeight;
	ASSERT( !m_bAlphaLayerExist );
	m_bAlphaLayerExist = true;
unsigned char
		_uc0, _uc1, _uc2,
		*pData = (unsigned char *)m_pData;
	for( int i = 0; i < nPixelCount; i++ )
	{
		_uc0 = pData[ 4*i + 0 ];
		_uc1 = pData[ 4*i + 1 ];
		_uc2 = pData[ 4*i + 2 ];
		pData[ 4*i + 0 ] = _uc2;
		pData[ 4*i + 1 ] = _uc1;
		pData[ 4*i + 2 ] = _uc0;
		pData[ 4*i + 3 ] = nAlphaClearVal;
	}
	return true;
}

bool C3DTexture::AddAlphaLayer(
	unsigned char nAlphaClearVal // = 255
	)
{
	if( IsEmpty() || m_bAlphaLayerExist )
		return false;
unsigned char * pDataOld = (unsigned char *)m_pData;
	ASSERT( pDataOld != NULL );
	ASSERT( m_sizeTexture.cx > 0 && m_sizeTexture.cy > 0 );
int nPixelCount = m_sizeTexture.cx * m_sizeTexture.cy;
unsigned char * pDataNew = new unsigned char[4*nPixelCount];
	for( int i = 0; i < nPixelCount; i++ )
	{
		pDataNew[ 4*i + 0 ] = pDataOld[ 3*i + 0 ];
		pDataNew[ 4*i + 1 ] = pDataOld[ 3*i + 1 ];
		pDataNew[ 4*i + 2 ] = pDataOld[ 3*i + 2 ];
		pDataNew[ 4*i + 3 ] = nAlphaClearVal;
	}
	delete [] pDataOld;
	pDataOld = pDataNew;
	return true;
}


bool C3DTexture::SetAlphaLayer(
	unsigned char nAlphaSetVal // = 255
	)
{
	if( IsEmpty() || (!m_bAlphaLayerExist) )
		return false;
unsigned char * pData = (unsigned char *)m_pData;
	ASSERT( pData != NULL );
	ASSERT( m_sizeTexture.cx > 0 && m_sizeTexture.cy > 0 );
int nPixelCount = m_sizeTexture.cx * m_sizeTexture.cy;
	for( int i = 0; i < nPixelCount; i++ )
		pData[ 4*i + 3 ] = nAlphaSetVal;
	return true;
}

bool C3DTexture::SetAlphaLayerIf(
	unsigned char nAlphaSetVal,
	COLORREF clr
	)
{
	if( IsEmpty() || (!m_bAlphaLayerExist) )
		return false;
unsigned char * pData = (unsigned char *)m_pData;
	ASSERT( pData != NULL );
	ASSERT( m_sizeTexture.cx > 0 && m_sizeTexture.cy > 0 );
int nPixelCount = m_sizeTexture.cx * m_sizeTexture.cy;
unsigned char byteR = GetRValue(clr);
unsigned char byteG = GetGValue(clr);
unsigned char byteB = GetBValue(clr);
	for( int i = 0; i < nPixelCount; i++ )
	{
		if(		byteR == pData[ 4*i + 0 ]
			&&	byteG == pData[ 4*i + 1 ]
			&&	byteB == pData[ 4*i + 2 ]
			)
			pData[ 4*i + 3 ] = nAlphaSetVal;
	}
	return true;
}

bool C3DTexture::SetAlphaLayerNB()
{
	if( IsEmpty() || (!m_bAlphaLayerExist) )
		return false;
unsigned char * pData = (unsigned char *)m_pData;
	ASSERT( pData != NULL );
	ASSERT( m_sizeTexture.cx > 0 && m_sizeTexture.cy > 0 );
int nPixelCount = m_sizeTexture.cx * m_sizeTexture.cy;
	for( int i = 0; i < nPixelCount; i++ )
	{
		unsigned char byteR = pData[ 4*i + 0 ];
		pData[ 4*i + 3 ] = byteR;
	}
	return true;
}

/////////////////////////////////////////////////////////////////////////////
// C3DView

IMPLEMENT_DYNCREATE( C3DView, CObject );

C3DView::C3DView(
	HWND hWndOutput // = NULL
	)
	: m_hWndOutput( hWndOutput )
	, m_sizeView( GL_VIEWS_MIN_VIEW_DX, GL_VIEWS_MIN_VIEW_DY )
	, m_sizeViewNE( GL_VIEWS_MIN_VIEW_DX, GL_VIEWS_MIN_VIEW_DY )
	, m_hOpenGlContext( NULL )
	, m_nCameraIndex( 0L )
	, m_strViewName( _T("noname") )
{
	ASSERT( m_hWndOutput != NULL );
	ASSERT( ::IsWindow(m_hWndOutput) );
	_Init();
}

C3DView::~C3DView()
{
	_Done();
}

bool C3DView::IsViewVisible() const
{
	ASSERT_VALID( this );
	ASSERT( m_hWndOutput != NULL );
	ASSERT( ::IsWindow( ((HWND)m_hWndOutput) ) );
	if( ::IsWindowVisible( ((HWND)m_hWndOutput) ) )
	{
		return (
			::SendMessage(
				m_hWndOutput,
				GL_VIEWS_WM_QUERY_VIEW_VISIBILITY,
				0L,
				0L
				) != 0
			) ? true : false;
//		HWND hWndParent = ::GetParent( m_hWndOutput );
//		ASSERT( hWndParent != NULL );
//		ASSERT( ::IsWindow( hWndParent ) );
//		if( ::IsWindowVisible( hWndParent ) )
//			return true;
	}
	return false;
}

void C3DView::Lock()
{
	ASSERT_VALID( this );
	m_csGDI.Lock();
}

void C3DView::Unlock()
{
	ASSERT_VALID( this );
	m_csGDI.Unlock();
}

void C3DView::_Init()
{
	m_dc.SetViewSize( m_sizeView, false );
	m_dc.GetInternalBitmap();
	ASSERT( m_dc.GetSafeHdc() != NULL );
	if( m_hOpenGlContext != NULL )
		return;
static PIXELFORMATDESCRIPTOR pfd =
{
	sizeof(PIXELFORMATDESCRIPTOR),
	1,									
	PFD_SUPPORT_OPENGL|PFD_DRAW_TO_BITMAP,
	PFD_TYPE_RGBA,
	OnGlGetBufferBits(__EBB_COLOR),
	0, 0, 0, 0, 0, 0,
	OnGlGetBufferBits(__EBB_ALPHA),
	0,
	OnGlGetBufferBits(__EBB_ACCUM),
	0, 0, 0, 0,
	OnGlGetBufferBits(__EBB_Z),
	OnGlGetBufferBits(__EBB_STENCIL),
	OnGlGetBufferBits(__EBB_AUXILIARY),
	PFD_MAIN_PLANE,
	0,
	0, 0, 0
};
GLuint PixelFormat = ChoosePixelFormat( m_dc.GetSafeHdc(), &pfd );
	if(	!PixelFormat )
	{
		OnGlAlertDisplay( _T("Failed to choose the pixel format") );
		ASSERT( FALSE );
		return;
	}
	if( !SetPixelFormat( m_dc.GetSafeHdc(), PixelFormat, &pfd ) )
	{
		OnGlAlertDisplay( _T("Failed to set the pixel format") );
		ASSERT( FALSE );
		return;
	}
	m_hOpenGlContext = wglCreateContext( m_dc.GetSafeHdc() );
	if( m_hOpenGlContext == NULL )
	{
		OnGlAlertDisplay( _T("Failed to create OpenGL rendering context") );
		ASSERT( FALSE );
		return;
	}

	if( !wglMakeCurrent( m_dc.GetSafeHdc(), m_hOpenGlContext ) )
	{
		OnGlAlertDisplay( _T("Failed to activate OpenGL rendering context") );
		ASSERT( FALSE );
		return;
	}
struct
{
	LPCTSTR m_lpszResource;
} _texture_init_data[GL_VIEWS_TEXTURE_COUNT] =
{
	{ MAKEINTRESOURCE(IDB_BITMAP_CUBE_ORANGE) },
	{ MAKEINTRESOURCE(IDB_BITMAP_CUBE_WHITE) },
	{ MAKEINTRESOURCE(IDB_BITMAP_RING) },
	{ MAKEINTRESOURCE(IDB_BITMAP_BOTTOM_PLANE) },
	{ MAKEINTRESOURCE(IDB_BITMAP_WND_AVI_PLAYER) },
	{ MAKEINTRESOURCE(IDB_BITMAP_WND_AVI_PLAYER) }, // for surface
	{ MAKEINTRESOURCE(IDB_BITMAP_WND_MIRROR) },
	{ MAKEINTRESOURCE(IDB_BITMAP_EARTH) },
	{ MAKEINTRESOURCE(IDB_BITMAP_MOON) },
};
	
	glEnable( GL_TEXTURE_2D );
	GL_VIEWS_CHECK_OPENGL_ERROR

	for( int nTextureIdx = 0; nTextureIdx < GL_VIEWS_TEXTURE_COUNT; nTextureIdx++ )
	{
		C3DTexture objTexture;
		if( !objTexture.LoadResourceBitmapAs32Bit(
				_texture_init_data[nTextureIdx].m_lpszResource
				)
			)
		{
			ASSERT( FALSE );
			return;
		}
		ASSERT(	objTexture.IsAlphaLayerExist() );
		objTexture.SetAlphaLayerIf( 180, RGB(245,21,3) );
		objTexture.SetAlphaLayerIf( 0, RGB(0,0,0) );
		glGenTextures( 1, &m_TextureIds[nTextureIdx] );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glBindTexture( GL_TEXTURE_2D, m_TextureIds[nTextureIdx] );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glTexImage2D(
			GL_TEXTURE_2D,
			0,
			GL_RGBA,
			objTexture.GetWidth(),
			objTexture.GetHeight(),
			0,
			GL_RGBA,
			GL_UNSIGNED_BYTE,
			objTexture.GetData()
			);
		GL_VIEWS_CHECK_OPENGL_ERROR
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
		GL_VIEWS_CHECK_OPENGL_ERROR
	}

	glDisable( GL_TEXTURE_2D );
	GL_VIEWS_CHECK_OPENGL_ERROR

	ASSERT( !m_Font3D.IsFontCreated() );
	VERIFY( m_Font3D.CreateFont( m_dc.GetSafeHdc() ) );
	
	if( !wglMakeCurrent( NULL, NULL ) )
	{
		OnGlAlertDisplay( _T("Failed to release contexts") );
		ASSERT( FALSE );
		return;
	}
}

void C3DView::_Done()
{
	m_Font3D.DeleteFont();
	if( m_hOpenGlContext != NULL )
	{
		if( !wglDeleteContext( m_hOpenGlContext ) )
		{
			OnGlAlertDisplay( _T("Failed to release rendering context") );
			ASSERT( FALSE );
		}
		m_hOpenGlContext = NULL;
	} // if( m_hOpenGlContext != NULL )
	m_dc.DeleteDC();
}

void C3DView::SetViewSize( CSize sizeView )
{
	ASSERT_VALID( this );
	ASSERT( m_sizeView.cx >= GL_VIEWS_MIN_VIEW_DX && m_sizeView.cy >= GL_VIEWS_MIN_VIEW_DY );
	ASSERT( m_sizeViewNE.cx >= GL_VIEWS_MIN_VIEW_DX && m_sizeViewNE.cy >= GL_VIEWS_MIN_VIEW_DY );
	Lock();
	m_sizeViewNE.cx = max( GL_VIEWS_MIN_VIEW_DX, sizeView.cx );
	m_sizeViewNE.cy = max( GL_VIEWS_MIN_VIEW_DY, sizeView.cy );
	Unlock();
}

void C3DView::AdjustViewSize(
	bool bLockView // = true
	)
{
	ASSERT_VALID( this );
	if(	m_sizeView == m_sizeViewNE )
		return;
	if( bLockView )
		Lock();
	ASSERT( m_sizeView.cx >= GL_VIEWS_MIN_VIEW_DX && m_sizeView.cy >= GL_VIEWS_MIN_VIEW_DY );
	ASSERT( m_sizeViewNE.cx >= GL_VIEWS_MIN_VIEW_DX && m_sizeViewNE.cy >= GL_VIEWS_MIN_VIEW_DY );
	_Done();
	m_sizeView = m_sizeViewNE;
	_Init();
	if( bLockView )
		Unlock();
}

void C3DView::DoStepLeft( GLfloat fStepSize )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoStepLeft( fStepSize );
	Unlock();
}

void C3DView::DoStepUp( GLfloat fStepSize )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoStepUp( fStepSize );
	Unlock();
}

void C3DView::DoStepForward( GLfloat fStepSize )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoStepForward( fStepSize );
	Unlock();
}

void C3DView::DoLookLeft( GLfloat fAngleDegrees )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoLookLeft( fAngleDegrees );
	Unlock();
}

void C3DView::DoLookUp( GLfloat fAngleDegrees )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoLookUp( fAngleDegrees );
	Unlock();
}

void C3DView::DoLookOwnAxis( GLfloat fAngleDegrees )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoLookOwnAxis( fAngleDegrees );
	Unlock();
}

/////////////////////////////////////////////////////////////////////////////
// C3DPipeThread

C3DPipeThread the3DPipe;

C3DPipeThread::C3DPipeThread()
	: m_bInitComplete( false )
	, m_eventShutdownStart( FALSE, FALSE, NULL, NULL )
	, m_eventShutdownComplete( FALSE, FALSE, NULL, NULL )
	, m_eventRenderViews( FALSE, FALSE, NULL, NULL )
	, m_pRoot( new C3DObject(GL_VIEWS_ROOTNAME) )
	, m_bTimerAnimationEnabled( false )
{
	m_bAutoDelete = FALSE;
}

C3DPipeThread::~C3DPipeThread()
{
	ASSERT( !m_bAutoDelete );
	_ResourcesFree();
	m_arrCams.RemoveAll();
	delete m_pRoot;
}

bool C3DPipeThread::_ResourcesInit()
{
	return true;
}

void C3DPipeThread::_ResourcesFree()
{
int nViewCount = m_arrViews.GetSize();
	for( int nViewIdx = 0; nViewIdx < nViewCount; nViewIdx++ )
	{
		C3DView * pView3D = m_arrViews[nViewIdx];
		ASSERT_VALID( pView3D );
		delete pView3D;
	}
	m_arrViews.RemoveAll();

	m_bInitComplete = false;
}

C3DObject * C3DPipeThread::GetRoot()
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	return m_pRoot;
}

const C3DObject * C3DPipeThread::GetRoot() const
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	return m_pRoot;
}

void C3DPipeThread::AddView( C3DView * pView3D )
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	ASSERT_VALID( pView3D );
	m_arrViews.Add( pView3D );
}

int C3DPipeThread::GetViewCount() const
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	return m_arrViews.GetSize();
}

C3DView * C3DPipeThread::GetView( int nIndex )
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	ASSERT( 0 <= nIndex && nIndex < m_arrViews.GetSize() );
C3DView * pView3D = m_arrViews[nIndex];
	ASSERT_VALID( pView3D );
	return pView3D;
}


void C3DPipeThread::AddCamera( C3DCamera * pCam )
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	ASSERT_VALID( pCam );
	ASSERT( !m_bInitComplete );
	m_pRoot->AddChild( pCam );
	m_arrCams.Add( pCam );
}

int C3DPipeThread::GetCameraCount() const
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	return m_arrCams.GetSize();
}

C3DCamera * C3DPipeThread::GetCamera( int nIndex )
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	ASSERT( 0 <= nIndex && nIndex < m_arrCams.GetSize() );
C3DCamera * pCam = m_arrCams[nIndex];
	ASSERT_VALID( pCam );
	return pCam;
}

void C3DPipeThread::Render()
{
	m_eventRenderViews.SetEvent();
}

void C3DPipeThread::ShutdownAndWaitFor()
{
	if( !m_bInitComplete )
		return;
	m_eventShutdownStart.SetEvent();
	for(	CExtPopupMenuWnd::PassMsgLoop(false);
			!m_eventShutdownComplete.Lock(500);
			CExtPopupMenuWnd::PassMsgLoop(false)
		);
}

BOOL C3DPipeThread::InitInstance()
{
	if( !_ResourcesInit() )
	{
		ASSERT( FALSE );
		return FALSE;
	}
	ASSERT( m_arrViews.GetSize() > 0 );
	m_bInitComplete = true;

	return TRUE;
}

void C3DPipeThread::Delete()
{
	ASSERT( !m_bAutoDelete );
	_ResourcesFree();
	CWinThread::Delete();
}

int C3DPipeThread::Run()
{
	ASSERT( m_bInitComplete );

	ASSERT_VALID( m_pRoot );
	ObjectWriteAccessSet( true );
		m_pRoot->WalkTree(
			C3DObject::EWTQ_THREAD_INIT,
			NULL,
			NULL,
			NULL,
			NULL
			);
	ObjectWriteAccessSet( false );

int nViewCount = m_arrViews.GetSize();
	ASSERT( nViewCount > 0 );

CSyncObject * arrToWait[2] =
{
	&m_eventShutdownStart,
	&m_eventRenderViews
};
CMultiLock _ml( arrToWait, 2, FALSE );
	for( ; true; )
	{
		DWORD dwWaitResult = _ml.Lock( INFINITE, FALSE, 0 );
		if( dwWaitResult != (WAIT_OBJECT_0 + 1) )
			break;
		bool bResetWriteAccess = false;
		for( int nViewIdx = 0; nViewIdx < nViewCount; nViewIdx++ )
		{
			C3DView * pView3D = m_arrViews[nViewIdx];
			ASSERT_VALID( pView3D );
			if( !pView3D->IsViewVisible() )
				continue;
			bool bSendMsgComplete = false;
			pView3D->Lock();
			ASSERT( !pView3D->m_strViewName.IsEmpty() );
			pView3D->AdjustViewSize( false );
			ASSERT( pView3D->Get3DDC().GetSafeHdc() != NULL );
			if( pView3D->Get3DDC().GetSafeHdc() != NULL )
			{
				if( !bResetWriteAccess )
				{
					ObjectWriteAccessSet( true );
					bResetWriteAccess = true;
				}
				ASSERT( GetCameraCount() > 0 );
				C3DCamera * pCam = GetCamera( pView3D->GetCameraIndex() );
				ASSERT_VALID( pCam );
				m_pRoot->WalkTree(
					C3DObject::EWTQ_TRANSFORM,
					pCam,
					pView3D,
					NULL,
					NULL
					);
				if( !wglMakeCurrent( pView3D->Get3DDC().GetSafeHdc(), pView3D->GetOpenGlContext() ) )
				{
					pView3D->OnGlAlertDisplay( _T("Failed to activate OpenGL rendering context") );
					ASSERT( FALSE );
					continue;
				} // if( !wglMakeCurrent( pView3D->Get3DDC().GetSafeHdc(), pView3D->GetOpenGlContext() ) )
				else
				{
#ifdef _DEBUG
					static int g_nRenderCounter = 0;
					TRACE2( "    >>> 3D-PIPE: rendering %s images (counter=%d)\n", (LPCTSTR)pView3D->m_strViewName, g_nRenderCounter );
					g_nRenderCounter++;
#endif // _DEBUG
					glShadeModel( GL_SMOOTH );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glClearDepth( 1.0f );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glEnable( GL_DEPTH_TEST );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glDepthFunc( GL_LEQUAL );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glDepthMask( GL_TRUE );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glClearStencil( 0 );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glEnable( GL_CULL_FACE );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glCullFace( GL_FRONT );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glFrontFace( GL_CW );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glClear(
						GL_COLOR_BUFFER_BIT
						|GL_DEPTH_BUFFER_BIT
						|GL_STENCIL_BUFFER_BIT
						);
					GL_VIEWS_CHECK_OPENGL_ERROR
					glColorMask( 1, 1, 1, 1 );
					GL_VIEWS_CHECK_OPENGL_ERROR
					CSize _sizeView3D = pView3D->GetViewSize();
					pCam->m_fAspect =
						((GLfloat)_sizeView3D.cx)
						/ ((GLfloat)_sizeView3D.cy)
						;
					glViewport(
						0,
						0,
						_sizeView3D.cx,
						_sizeView3D.cy
						);
					GL_VIEWS_CHECK_OPENGL_ERROR
					glMatrixMode( GL_PROJECTION );
					GL_VIEWS_CHECK_OPENGL_ERROR
					_mt mtxPerspective;
					mtxPerspective.load_perspective(
						pCam->GetFov(),
						pCam->m_fAspect,
						pCam->m_fNearPlane,
						pCam->m_fFarPlane
						);
					glLoadMatrixf( mtxPerspective.arr );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glMatrixMode( GL_MODELVIEW );
					GL_VIEWS_CHECK_OPENGL_ERROR
					_mt _mtxCameraInversion( pCam->m_mtxLastTransformation );
					_mtxCameraInversion.load_inversion();
					glLoadMatrixf(_mtxCameraInversion.arr);
					GL_VIEWS_CHECK_OPENGL_ERROR
					m_pRoot->WalkTree(
						C3DObject::EWTQ_RENDER,
						pCam,
						pView3D,
						NULL,
						NULL
						);
					glFinish();
					GL_VIEWS_CHECK_OPENGL_ERROR
					if( !wglMakeCurrent( NULL, NULL ) )
					{
						pView3D->OnGlAlertDisplay( _T("Failed to release contexts") );
						ASSERT( FALSE );
					}
					bSendMsgComplete = true;
				} // else from if( !wglMakeCurrent( pView3D->Get3DDC().GetSafeHdc(), pView3D->GetOpenGlContext() ) )
			} // if( pView3D->Get3DDC().GetSafeHdc() != NULL )
			pView3D->Unlock();
			if( bSendMsgComplete )
			{
				if( bResetWriteAccess )
				{
					ObjectWriteAccessSet( false );
					bResetWriteAccess = false;
				}
				::SendMessage(
					pView3D->GetOutputHWND(),
					GL_VIEWS_WM_RENDER_FRAME_COMPLETE,
					0L,
					0L
					);
			}
		} // for( int nViewIdx = 0; nViewIdx < nViewCount; nViewIdx++ )
		if( m_bTimerAnimationEnabled )
		{
#ifdef _DEBUG
			static int g_nRecalcPosCounter = 0;
			TRACE1( "    >>> 3D-PIPE: recalculating object positions (counter=%d)\n", g_nRecalcPosCounter );
			g_nRecalcPosCounter++;
#endif // _DEBUG
			if( !bResetWriteAccess )
			{
				ObjectWriteAccessSet( true );
				bResetWriteAccess = true;
			}
			m_pRoot->WalkTree(
				C3DObject::EWTQ_PLAY,
				NULL,
				NULL,
				NULL,
				NULL
				);
		} // if( m_bTimerAnimationEnabled )
		if( bResetWriteAccess )
		{
			ObjectWriteAccessSet( false );
			bResetWriteAccess = false;
		}
	} // for( ; true; )
	
	ObjectWriteAccessSet( true );
		m_pRoot->WalkTree(
			C3DObject::EWTQ_THREAD_DONE,
			NULL,
			NULL,
			NULL,
			NULL
			);
	ObjectWriteAccessSet( false );
	
	_ResourcesFree();
	m_eventShutdownComplete.SetEvent();
	return 0L;
}

/////////////////////////////////////////////////////////////////////////////
// CCameraSelectionComboBox

IMPLEMENT_DYNAMIC( CCameraSelectionComboBox, CExtComboBox )

CCameraSelectionComboBox::CCameraSelectionComboBox()
{
}

CCameraSelectionComboBox::~CCameraSelectionComboBox()
{
}

BEGIN_MESSAGE_MAP( CCameraSelectionComboBox, CExtComboBox )
	//{{AFX_MSG_MAP(CCameraSelectionComboBox)
	//}}AFX_MSG_MAP
	ON_CONTROL_REFLECT(CBN_SELENDOK,OnReflectCbnSelEndOK)
END_MESSAGE_MAP()

void CCameraSelectionComboBox::SyncCameraWithSelectedItem(
	bool bSetFocusToView // = false
	)
{
	ASSERT_VALID( this );
CWnd * pWnd = GetParent();
	ASSERT_VALID( pWnd );
	ASSERT_KINDOF( CExtToolControlBar, pWnd );
CChildView * pChildView = STATIC_DOWNCAST( CChildView, pWnd->GetParent() );
	ASSERT_VALID( pChildView );
C3DView * pView3D = pChildView->m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
int nCurSel = GetCurSel();
	ASSERT( 0 <= nCurSel && nCurSel < GL_VIEWS_CAMERA_COUNT );
int nCamIdxOld = pView3D->GetCameraIndex();
	ASSERT( 0 <= nCamIdxOld && nCamIdxOld < GL_VIEWS_CAMERA_COUNT );
	if( nCamIdxOld != nCurSel )
	{
		pView3D->SetCameraIndex( nCurSel );
	} // if( nCamIdxOld != nCurSel )
	if( bSetFocusToView )
		pChildView->SetFocus();
	if( nCamIdxOld != nCurSel )
	{
		CFrameWnd * pFrame = GetParentFrame();
		ASSERT_VALID( pFrame );
		CMainFrame * pMainFrame =
			DYNAMIC_DOWNCAST( CMainFrame, pFrame );
		if( pMainFrame == NULL )
		{
			pMainFrame =
				DYNAMIC_DOWNCAST( CMainFrame, pFrame->GetParentFrame() );
			ASSERT_VALID( pMainFrame );
		}
		pMainFrame->SyncCameraFovValue();
	} // if( nCamIdxOld != nCurSel )
	the3DPipe.Render();
}

int CCameraSelectionComboBox::SetCurSel(
	int nSelect,
	bool bSetFocusToView // = false
	)
{
	ASSERT( 0 <= nSelect && nSelect < GL_VIEWS_CAMERA_COUNT );
int nRetVal = CExtComboBox::SetCurSel( nSelect );
	SyncCameraWithSelectedItem( bSetFocusToView );
	return nRetVal;
}

void CCameraSelectionComboBox::OnReflectCbnSelEndOK()
{
	ASSERT_VALID( this );
bool bSetFocusToView = false;
HWND hWndFocus = ::GetFocus();
	if( hWndFocus == m_hWnd )
		bSetFocusToView = true;
	SyncCameraWithSelectedItem( bSetFocusToView );
}


/////////////////////////////////////////////////////////////////////////////
// CCameraFovComboBox

IMPLEMENT_DYNAMIC( CCameraFovComboBox, CExtComboBox )

CCameraFovComboBox::CCameraFovComboBox()
{
}

CCameraFovComboBox::~CCameraFovComboBox()
{
}

BEGIN_MESSAGE_MAP( CCameraFovComboBox, CExtComboBox )
	//{{AFX_MSG_MAP(CCameraFovComboBox)
	//}}AFX_MSG_MAP
	ON_CONTROL_REFLECT(CBN_SELENDOK,OnReflectCbnSelEndOK)
END_MESSAGE_MAP()

void CCameraFovComboBox::SyncCameraWithSelectedItem(
	bool bSetFocusToView // = false
	)
{
	ASSERT_VALID( this );
CWnd * pWnd = GetParent();
	ASSERT_VALID( pWnd );
	ASSERT_KINDOF( CExtToolControlBar, pWnd );
CChildView * pChildView = STATIC_DOWNCAST( CChildView, pWnd->GetParent() );
	ASSERT_VALID( pChildView );
C3DView * pView3D = pChildView->m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
int nCamIdx = pView3D->GetCameraIndex();
	ASSERT( 0 <= nCamIdx && nCamIdx < GL_VIEWS_CAMERA_COUNT );
C3DCamera * pCam = the3DPipe.GetCamera( nCamIdx );
	ASSERT_VALID( pCam );

int nFovIdx = GetCurSel();
	ASSERT( 0 <= nFovIdx && nFovIdx < GL_VIEWS_FOV_COUNT );

bool bRender = false;
	the3DPipe.ObjectWriteAccessSet( true );
		if( pCam->m_nFovIndex != nFovIdx ) 
		{
			pCam->m_nFovIndex = nFovIdx;
			bRender = true;
		}
	the3DPipe.ObjectWriteAccessSet( false );

	if( bSetFocusToView )
		pChildView->SetFocus();
	if( bRender )
	{
		CFrameWnd * pFrame = GetParentFrame();
		ASSERT_VALID( pFrame );
		CMainFrame * pMainFrame =
			DYNAMIC_DOWNCAST( CMainFrame, pFrame );
		if( pMainFrame == NULL )
		{
			pMainFrame =
				DYNAMIC_DOWNCAST( CMainFrame, pFrame->GetParentFrame() );
			ASSERT_VALID( pMainFrame );
		}
		pMainFrame->SyncCameraFovValue( nCamIdx, nFovIdx );
		the3DPipe.Render();
	}
}

int CCameraFovComboBox::SetCurSel(
	int nSelect,
	bool bSetFocusToView // = false
	)
{
	ASSERT( 0 <= nSelect && nSelect < GL_VIEWS_FOV_COUNT );
int nRetVal = CExtComboBox::SetCurSel( nSelect );
	SyncCameraWithSelectedItem( bSetFocusToView );
	return nRetVal;
}

void CCameraFovComboBox::OnReflectCbnSelEndOK()
{
	ASSERT_VALID( this );
bool bSetFocusToView = false;
HWND hWndFocus = ::GetFocus();
	if( hWndFocus == m_hWnd )
		bSetFocusToView = true;
	SyncCameraWithSelectedItem( bSetFocusToView );
}


/////////////////////////////////////////////////////////////////////////////
// CObjectHierarchyTreeCtrl

IMPLEMENT_DYNAMIC( CObjectHierarchyTreeCtrl, CTreeCtrl )

CObjectHierarchyTreeCtrl::CObjectHierarchyTreeCtrl()
{
}

CObjectHierarchyTreeCtrl::~CObjectHierarchyTreeCtrl()
{
}

BEGIN_MESSAGE_MAP( CObjectHierarchyTreeCtrl, CTreeCtrl )
	//{{AFX_MSG_MAP(CObjectHierarchyTreeCtrl)
	ON_WM_LBUTTONDBLCLK()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CObjectHierarchyTreeCtrl message handlers

void CObjectHierarchyTreeCtrl::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	CTreeCtrl::OnLButtonDblClk(nFlags, point);
UINT htFlags = 0L;
HTREEITEM _hti = CTreeCtrl::HitTest( point, &htFlags );
	if(		_hti != NULL
		&&	(htFlags&(TVHT_ONITEM)) != 0
		)
	{
		C3DObject * pObj = (C3DObject *)
			CTreeCtrl::GetItemData( _hti );
		ASSERT( pObj != NULL );
		ASSERT_VALID( pObj );
		ASSERT_KINDOF( C3DObject, pObj );
		pObj->OnTreeItemDblClick( this );
	}

}

/////////////////////////////////////////////////////////////////////////////
// CChildView

IMPLEMENT_DYNAMIC( CChildView, CWnd )

GLfloat CChildView::g_fStepRotationAngle = 2.0f;
GLfloat CChildView::g_fStepWalkSize = 0.02f;

CChildView::CChildView(
	UINT nIdResourceCursor
	)
	: m_wndGlPanel( nIdResourceCursor )
{
	ASSERT( (ID_SELCAM9) == (ID_SELCAM8 + 1) );
	ASSERT( (ID_SELCAM8) == (ID_SELCAM7 + 1) );
	ASSERT( (ID_SELCAM7) == (ID_SELCAM6 + 1) );
	ASSERT( (ID_SELCAM6) == (ID_SELCAM5 + 1) );
	ASSERT( (ID_SELCAM5) == (ID_SELCAM4 + 1) );
	ASSERT( (ID_SELCAM4) == (ID_SELCAM3 + 1) );
	ASSERT( (ID_SELCAM3) == (ID_SELCAM2 + 1) );
	ASSERT( (ID_SELCAM2) == (ID_SELCAM1 + 1) );
	ASSERT( (ID_SELCAM1) == (ID_SELCAM0 + 1) );
}

CChildView::~CChildView()
{
}

BEGIN_MESSAGE_MAP( CChildView, CWnd )
	//{{AFX_MSG_MAP(CChildView)
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_CREATE()
	ON_WM_SIZE()
	ON_COMMAND(ID_ROTATE_ABOUT_X0, OnRotateAboutX0)
	ON_COMMAND(ID_ROTATE_ABOUT_X1, OnRotateAboutX1)
	ON_COMMAND(ID_ROTATE_ABOUT_Y0, OnRotateAboutY0)
	ON_COMMAND(ID_ROTATE_ABOUT_Y1, OnRotateAboutY1)
	ON_COMMAND(ID_ROTATE_ABOUT_Z0, OnRotateAboutZ0)
	ON_COMMAND(ID_ROTATE_ABOUT_Z1, OnRotateAboutZ1)
	ON_COMMAND(ID_TRANSLATE_X0, OnTranslateX0)
	ON_COMMAND(ID_TRANSLATE_X1, OnTranslateX1)
	ON_COMMAND(ID_TRANSLATE_Y0, OnTranslateY0)
	ON_COMMAND(ID_TRANSLATE_Y1, OnTranslateY1)
	ON_COMMAND(ID_TRANSLATE_Z0, OnTranslateZ0)
	ON_COMMAND(ID_TRANSLATE_Z1, OnTranslateZ1)
	ON_WM_SETFOCUS()
	ON_WM_CONTEXTMENU()
	ON_UPDATE_COMMAND_UI(ID_BTN_MENU_ROTATION, OnUpdateEnabledBtnInBar)
	ON_COMMAND(ID_CAM_FOV_INC, OnCamFovInc)
	ON_UPDATE_COMMAND_UI(ID_CAM_FOV_INC, OnUpdateCamFovInc)
	ON_COMMAND(ID_CAM_FOV_DEC, OnCamFovDec)
	ON_UPDATE_COMMAND_UI(ID_CAM_FOV_DEC, OnUpdateCamFovDec)
	//}}AFX_MSG_MAP
	
	ON_COMMAND_RANGE( ID_SELCAM0, ID_SELCAM9, OnSelectCamera )
	ON_UPDATE_COMMAND_UI_RANGE( ID_SELCAM0, ID_SELCAM9, OnUpdateSelectCamera )
	
	ON_UPDATE_COMMAND_UI( ID_CAMERA_SELECTION_COMBO, OnUpdateEnabledBtnInBar )
	ON_UPDATE_COMMAND_UI( ID_CAMERA_FOV_COMBO, OnUpdateEnabledBtnInBar )
	ON_UPDATE_COMMAND_UI( ID_BTN_MENU_ROTATION, OnUpdateEnabledBtnInBar )
	ON_UPDATE_COMMAND_UI( ID_BTN_MENU_TRANSLATION, OnUpdateEnabledBtnInBar )
	
	ON_REGISTERED_MESSAGE(
		CExtPopupMenuWnd::g_nMsgPrepareMenu,
		OnExtMenuPrepare
		)

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChildView message handlers

LRESULT CChildView::OnExtMenuPrepare(WPARAM wParam, LPARAM lParam)
{
	lParam;
CExtPopupMenuWnd::MsgPrepareMenuData_t * pData =
		reinterpret_cast
			< CExtPopupMenuWnd::MsgPrepareMenuData_t * >
			( wParam );
	ASSERT( pData != NULL );
CExtPopupMenuWnd * pPopup = pData->m_pPopup;
	ASSERT( pPopup != NULL );

	// remove camera selection combo command
INT nPos = pPopup->ItemFindPosForCmdID(ID_CAMERA_SELECTION_COMBO);
	if( nPos >= 0 )
		pPopup->ItemRemove( nPos );
	if(		pPopup->ItemGetCount() > 0
		&&	pPopup->ItemGetInfo(0).IsSeparator()
		)
		pPopup->ItemRemove( 0 );

	// replace camera FOV combo command
	nPos = pPopup->ItemFindPosForCmdID(ID_CAMERA_FOV_COMBO);
	if( nPos >= 0 )
	{
		pPopup->ItemRemove( nPos );
		if( pPopup->ItemInsert(
				(UINT)CExtPopupMenuWnd::TYPE_POPUP,
				nPos,
				_T("Camera FOV"),
				NULL,
				m_hWnd
				)
			)
		{
			CExtPopupMenuWnd * pSubMenu =
				pPopup->ItemGetPopup( nPos );
			ASSERT_VALID( pSubMenu );
			VERIFY(
				pSubMenu->ItemInsert(
					ID_CAM_FOV_INC,
					-1,
					NULL,
					NULL,
					m_hWnd
					)
				);
			VERIFY(
				pSubMenu->ItemInsert(
					ID_CAM_FOV_DEC,
					-1,
					NULL,
					NULL,
					m_hWnd
					)
				);
			UpdateDialogControls( this, TRUE );
		}
#ifdef _DEBUG
		else
		{
			ASSERT( FALSE );
		}
#endif // _DEBUG
	}
	
	// remove leading separator if exist
	if(		pPopup->ItemGetCount() > 0
		&&	pPopup->ItemGetInfo(0).IsSeparator()
		)
		pPopup->ItemRemove( 0 );

	return TRUE;
}

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) 
{
	if( ! (CExtWRB < CWnd > ::PreCreateWindow(cs) ) )
		return FALSE;

	cs.dwExStyle &= ~(WS_EX_STATICEDGE|WS_EX_CLIENTEDGE);
	cs.style &= ~WS_BORDER;
	cs.style |= WS_CLIPSIBLINGS;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 
		::LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_WINDOW+1), NULL);

	return TRUE;
}

void CChildView::OnPaint() 
{
CPaintDC dcPaint( this );
	CExtPaintManager::stat_ExcludeChildAreas(
		dcPaint.GetSafeHdc(),
		GetSafeHwnd()
		);
}

BOOL CChildView::OnEraseBkgnd(CDC* pDC) 
{
	pDC;
	return TRUE;
}


int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if( CExtWRB < CWnd >::OnCreate(lpCreateStruct) == -1 )
	{
		ASSERT( FALSE );
		return -1;
	}

	m_wndToolbar.m_bPresubclassDialogMode = true;
	if( !m_wndToolbar.Create(
			_T(""),
			this,
			AFX_IDW_DIALOGBAR,
			WS_CHILD|WS_VISIBLE
				|CBRS_ALIGN_TOP|CBRS_TOOLTIPS
				|CBRS_FLYBY|CBRS_SIZE_DYNAMIC
			)
		)
	{
		ASSERT( FALSE );
		return -1;
	}
	if( !m_wndToolbar.LoadToolBar(IDR_TOOLBAR_INVIEW) )
	{
		ASSERT( FALSE );
		return -1;
	}
	if( !m_wndToolbar.InitNavigationBar() )
		return -1;
	
	if(	!m_wndGlPanel.Create(
			this,
			GL_VIEWS_ID_VIEW_DLG_CTRL_ID
			)
		)
	{
		ASSERT( FALSE );
		return -1;
	}
	m_wndGlPanel.SetFont( CFont::FromHandle((HFONT)::GetStockObject(DEFAULT_GUI_FONT)) );

	CWnd::RepositionBars( 0, 0xFFFF, GL_VIEWS_ID_VIEW_DLG_CTRL_ID );
	return 0;
}


void CChildView::OnSize(UINT nType, int cx, int cy) 
{
	CExtWRB < CWnd > ::OnSize(nType, cx, cy);
	if( nType != SIZE_MINIMIZED )
		CWnd::RepositionBars( 0, 0xFFFF, GL_VIEWS_ID_VIEW_DLG_CTRL_ID );
}

void CChildView::OnUpdateEnabledBtnInBar(CCmdUI* pCmdUI) 
{
	ASSERT( pCmdUI != NULL );
	pCmdUI->Enable();
}

void CChildView::OnSetFocus(CWnd* pOldWnd) 
{
	CWnd ::OnSetFocus(pOldWnd);
	m_wndGlPanel.SetFocus();
}

void CChildView::OnContextMenu(CWnd* pWnd, CPoint point) 
{
	pWnd;
	point;
}

void CChildView::OnRotateAboutX0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookUp( g_fStepRotationAngle );
	the3DPipe.Render();
}
void CChildView::OnRotateAboutX1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookUp( -g_fStepRotationAngle );
	the3DPipe.Render();
}

void CChildView::OnRotateAboutY0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookLeft( g_fStepRotationAngle );
	the3DPipe.Render();
}
void CChildView::OnRotateAboutY1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookLeft( -g_fStepRotationAngle );
	the3DPipe.Render();
}

void CChildView::OnRotateAboutZ0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookOwnAxis( g_fStepRotationAngle );
	the3DPipe.Render();
}
void CChildView::OnRotateAboutZ1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookOwnAxis( -g_fStepRotationAngle );
	the3DPipe.Render();
}

void CChildView::OnTranslateX0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepLeft( g_fStepWalkSize );
	the3DPipe.Render();
}
void CChildView::OnTranslateX1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepLeft( -g_fStepWalkSize );
	the3DPipe.Render();
}

void CChildView::OnTranslateY0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepUp( g_fStepWalkSize );
	the3DPipe.Render();
}
void CChildView::OnTranslateY1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepUp( -g_fStepWalkSize );
	the3DPipe.Render();
}

void CChildView::OnTranslateZ0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepForward( g_fStepWalkSize );
	the3DPipe.Render();
}
void CChildView::OnTranslateZ1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepForward( -g_fStepWalkSize );
	the3DPipe.Render();
}

void CChildView::OnSelectCamera( UINT nCmdID )
{
	ASSERT_VALID( this );
	ASSERT( ID_SELCAM0 <= nCmdID && nCmdID <= ID_SELCAM9 );
int nCameraIndex = nCmdID - ID_SELCAM0;
	ASSERT( 0 <= nCameraIndex && nCameraIndex < GL_VIEWS_CAMERA_COUNT );
	m_wndToolbar.m_wndCameraSelCombo.SetCurSel( nCameraIndex );
}
void CChildView::OnUpdateSelectCamera(CCmdUI* pCmdUI)
{
	ASSERT_VALID( this );
	ASSERT( pCmdUI != NULL );
	ASSERT( ID_SELCAM0 <= pCmdUI->m_nID && pCmdUI->m_nID <= ID_SELCAM9 );
int nCameraIndexReal = m_wndToolbar.m_wndCameraSelCombo.GetCurSel();
	if( nCameraIndexReal < 0 )
	{
		pCmdUI->Enable( FALSE );
		return;
	}
	ASSERT( 0 <= nCameraIndexReal && nCameraIndexReal < GL_VIEWS_CAMERA_COUNT );
int nCameraIndexToCmp = pCmdUI->m_nID - ID_SELCAM0;
	ASSERT( 0 <= nCameraIndexToCmp && nCameraIndexToCmp < GL_VIEWS_CAMERA_COUNT );
	pCmdUI->Enable( TRUE );
	pCmdUI->SetRadio(
		(nCameraIndexReal == nCameraIndexToCmp)
			? TRUE
			: FALSE
		);
}


void CChildView::OnCamFovInc() 
{
	ASSERT_VALID( this );
int nCameraIndexReal = m_wndToolbar.m_wndCameraSelCombo.GetCurSel();
	ASSERT(
			( 0 <= nCameraIndexReal && nCameraIndexReal < GL_VIEWS_CAMERA_COUNT )
		||	nCameraIndexReal < 0
		);
	if( nCameraIndexReal < 0 )
		return;
C3DCamera * pCam = the3DPipe.GetCamera( nCameraIndexReal );
	ASSERT_VALID( pCam );
int nFovIdx = pCam->m_nFovIndex;
	ASSERT( 0 <= nFovIdx && nFovIdx < GL_VIEWS_FOV_COUNT );
	if( nFovIdx == (GL_VIEWS_FOV_COUNT-1) )
		return;
	m_wndToolbar.m_wndCameraFovCombo.SetCurSel( nFovIdx+1, false );
}
void CChildView::OnUpdateCamFovInc(CCmdUI* pCmdUI) 
{
	ASSERT_VALID( this );
	ASSERT( pCmdUI != NULL );
int nCameraIndexReal = m_wndToolbar.m_wndCameraSelCombo.GetCurSel();
	ASSERT(
			( 0 <= nCameraIndexReal && nCameraIndexReal < GL_VIEWS_CAMERA_COUNT )
		||	nCameraIndexReal < 0
		);
	if( nCameraIndexReal < 0 )
	{
		pCmdUI->Enable( FALSE );
		return;
	}
C3DCamera * pCam = the3DPipe.GetCamera( nCameraIndexReal );
	ASSERT_VALID( pCam );
int nFovIdx = pCam->m_nFovIndex;
	ASSERT( 0 <= nFovIdx && nFovIdx < GL_VIEWS_FOV_COUNT );
	pCmdUI->Enable(
		(nFovIdx < (GL_VIEWS_FOV_COUNT-1)) ? TRUE : FALSE
		);
}

void CChildView::OnCamFovDec() 
{
	ASSERT_VALID( this );
int nCameraIndexReal = m_wndToolbar.m_wndCameraSelCombo.GetCurSel();
	ASSERT(
			( 0 <= nCameraIndexReal && nCameraIndexReal < GL_VIEWS_CAMERA_COUNT )
		||	nCameraIndexReal < 0
		);
	if( nCameraIndexReal < 0 )
		return;
C3DCamera * pCam = the3DPipe.GetCamera( nCameraIndexReal );
	ASSERT_VALID( pCam );
int nFovIdx = pCam->m_nFovIndex;
	ASSERT( 0 <= nFovIdx && nFovIdx < GL_VIEWS_FOV_COUNT );
	if( nFovIdx == 0 )
		return;
	m_wndToolbar.m_wndCameraFovCombo.SetCurSel( nFovIdx-1, false );
}
void CChildView::OnUpdateCamFovDec(CCmdUI* pCmdUI) 
{
	ASSERT_VALID( this );
	ASSERT( pCmdUI != NULL );
int nCameraIndexReal = m_wndToolbar.m_wndCameraSelCombo.GetCurSel();
	ASSERT(
			( 0 <= nCameraIndexReal && nCameraIndexReal < GL_VIEWS_CAMERA_COUNT )
		||	nCameraIndexReal < 0
		);
	if( nCameraIndexReal < 0 )
	{
		pCmdUI->Enable( FALSE );
		return;
	}
C3DCamera * pCam = the3DPipe.GetCamera( nCameraIndexReal );
	ASSERT_VALID( pCam );
int nFovIdx = pCam->m_nFovIndex;
	ASSERT( 0 <= nFovIdx && nFovIdx < GL_VIEWS_FOV_COUNT );
	pCmdUI->Enable(
		(nFovIdx > 0) ? TRUE : FALSE
		);
}